arbitrary array index range?

E

ES Kim

"A Book on C" explains a technique to use an arbitrary array index range.

int* p = malloc(sizeof(int) * 10) - 1;

This way, the allocated array can be accessed with index range 1 ~ 10,
not 0 ~ 9. You cannot use p[0], of course, and the memory should be
released with free(p + 1).
It worked with gcc and MSVC, but I'm not sure if it makes sense to take
the address before the initial element of an array. Does anyone have
any definite answer?
 
S

S.Tobias

ES Kim said:
"A Book on C" explains a technique to use an arbitrary array index range.
int* p = malloc(sizeof(int) * 10) - 1;

Bad book.
This way, the allocated array can be accessed with index range 1 ~ 10,
not 0 ~ 9. You cannot use p[0], of course, and the memory should be
released with free(p + 1).

Whats wrong with indexes starting from zero? I find it the most
natural way ever, since I quit Pascal.

If you really-really need to index from 1, then waste one element:
int* p = malloc(sizeof(int) * (10 + 1));
It worked with gcc and MSVC, but I'm not sure if it makes sense to take
the address before the initial element of an array. Does anyone have
any definite answer?

Pure coincidence. Undefined behaviour.

A pointer can point into an array object or one past the end.
 
R

Richard Bos

ES Kim said:
correction: int* p = (int*)malloc(sizeof(int) * 10) - 1;

Not only is the original wrong, so is the correction. You cannot
portably set a pointer to the location before the start of an object.
Both of your lines above could cause segmentation faults, or if you're
less lucky, more unobvious symptoms of undefined behaviour.

Richard
 
T

Thomas Stegen

This is invalid and invokes undefined behaviour. Consider what might
happen if the pointer returned by malloc refers to memory which is at
the beginning of a segment for example. The C standard says this is
undefined behaviour because pointer arithmetic on a pointer (in this
case the pointer returned by malloc) must result in a pointer which
points somewhere in the same array or one past the end of that array.

Do not do this. If for some reason you want 1 based arrays just allocate
one extra element and ignore the first one. Usually this is not very
helpful though.
correction: int* p = (int*)malloc(sizeof(int) * 10) - 1;

Err, no.

Recommended malloc usage is something along the lines of

(T is an arbitrary type)

T *ptr = malloc(num_elements * sizeof *ptr);

Notice, no cast and no explicit dependency on the type of ptr
in the argument to malloc. Good Thing!

Search the archives for copious discussion on this.

HTH.
 
F

Flash Gordon

"A Book on C" explains a technique to use an arbitrary array index
range.

int* p = malloc(sizeof(int) * 10) - 1;

This way, the allocated array can be accessed with index range 1 ~ 10,
not 0 ~ 9. You cannot use p[0], of course, and the memory should be
released with free(p + 1).
It worked with gcc and MSVC, but I'm not sure if it makes sense to
take the address before the initial element of an array. Does anyone
have any definite answer?

The only values you are allowed to set a pointer to are:
1) The null pointer
2) Inside a valid C object
3) 1 passed the end of a valid C object.

1 less that the value returned by malloc is none of the above, so doing
that invokes the wrath of undefined behaviour and could cause Russia to
decide to attack you with nuclear missiles. So I would advise against
it.

Also, trying to simulate indexing from a value other than 0 in C
generally causes confusion for other programmers and makes the
introduction of errors. In languages such as Pascal which support
arbitrary indexing it is less likely to cause confusion because people
expect it.

As a final unrelated point, allocation using the form:
type *p = malloc(n * sizeof *p);
is generally less error prone since the type only appears in one place.
 
O

Old Wolf

Thomas Stegen said:
Err, no.

Recommended malloc usage is something along the lines of

Recommended, but not dogmatic. There are cases where it can
be appropriate to use a cast...
T *ptr = malloc(num_elements * sizeof *ptr);

In this case the cast is necessary, because you cannot do
arithmetic on void pointers (the first example should fail
to compile, or even worse, subtract 1 byte because some
compilers have extensions where void* arithmetic is treated
as char* arithmetic).
 
D

Dave Vandervies

Recommended, but not dogmatic. There are cases where it can
be appropriate to use a cast...


In this case the cast is necessary, because you cannot do
arithmetic on void pointers (the first example should fail
to compile, or even worse, subtract 1 byte because some
compilers have extensions where void* arithmetic is treated
as char* arithmetic).

True, but in this case, it doesn't matter if you invoke undefined behavior
by calling malloc without a prototype (causing the return type to be
incorrectly defaulted to int) and the compiler didn't warn you about
it because you told it to shut up with the cast, because you're already
invoking undefined behavior by creating a pointer off the beginning of
mallocd memory.

The solution is to Just Don't Do That, not to use the cast.


dave

--
Dave Vandervies (e-mail address removed)
I can't think of any programming language whose niche is larger than C's.
English as spoken by teachers in U.S. public schools.
--Dan Pop and Mike Wahler in comp.lang.c
 
E

E. Robert Tisdale

ES said:
"A Book on C" explains a technique to use an arbitrary array index range.

int* p = malloc(sizeof(int) * 10) - 1;

int* p = ((int*)malloc(10*sizeof(int))) - 1;
This way, the allocated array can be accessed with index range 1 ~ 10,
not 0 ~ 9. You cannot use p[0], of course,
and the memory should be released with free(p + 1).
It worked with gcc and MSVC,

And it will work with every ANSI/ISO compliant C compiler.
but I'm not sure if it makes sense to take
the address before the initial element of an array.
Does anyone have any definite answer?

According to the ANSI/ISO standards, p is undefined!
 
S

S.Tobias

And it will work with every ANSI/ISO compliant C compiler.
[snip]

According to the ANSI/ISO standards, p is undefined!

I envy those people who can write two such sentences together
and still live happily! :)
 
R

Richard Bos

E. Robert Tisdale said:
int* p = ((int*)malloc(10*sizeof(int))) - 1;

Doesn't help, although in this case the cast is at least conceptually
right. It's the subtraction which is the problem.
This way, the allocated array can be accessed with index range 1 ~ 10,
not 0 ~ 9. You cannot use p[0], of course,
and the memory should be released with free(p + 1).
It worked with gcc and MSVC,

And it will work with every ANSI/ISO compliant C compiler.

Care to explain why something that invokes undefined behaviour would
work with _every_ ISO C compiler?
According to the ANSI/ISO standards, p is undefined!

Quite.

Richard
 
M

Mark McIntyre

Recommended, but not dogmatic. There are cases where it can
be appropriate to use a cast...

Name one. Answer restricted to C code only.
In this case the cast is necessary, because you cannot do
arithmetic on void pointers (the first example should fail
to compile,

In the above case if ptr is of type void* then this should just fail to
compile end of story. If ptr is not of type void, then your comment doesn't
seem to make sense.
or even worse, subtract 1 byte because some
compilers have extensions where void* arithmetic is treated
as char* arithmetic)

What?
 
E

E. Robert Tisdale

Richard said:
Care to explain why something that invokes undefined behavior
would work with _every_ ISO C compiler?

The term "undefined behavior" means exactly that.
The ISO C standards do *not* specify the behavior
of pointers to objects outside of array bounds
so they are allowed to "work" and, in fact, they do work
for every compiler that complies with ISO C99.
 
O

Old Wolf

Mark McIntyre said:
In the above case if ptr is of type void* then this should just fail to
compile end of story. If ptr is not of type void, then your comment doesn't
seem to make sense.


What?

If we can return to the original case, which seems to have
been misquoted:

malloc(sizeof(int) * 10) - 1
That need not compile on a standards-conforming compiler.
Gcc warns:
g.c:5: warning: pointer of type `void *' used in arithmetic

Furthermore there is a common extension where void* arithmetic
is treated as char* arithmetic. You may not be familiar with
this; the upshot is that the above quote will compile
without warning and give the same result as:

(void *)(((char *)malloc(sizeof(int) * 10)) - 1)

[Note, if you care about the issue that it's undefined
to subtract from the pointer, change all '-' to '+']
 
M

Michael Wojcik

Name one. Answer restricted to C code only.

Casting an object pointer of another type to unsigned char * in order
to inspect an object's representation, or to copy it to another area
of memory, and so forth. (Note for the second case that we may be
using a freestanding implementation which does not provide memcpy and
memmove.) The alternative is to use an otherwise extraneous temporary
void *. I'd consider the cast in this situation preferable, and so
appropriate.

Casting the result of the sizeof operator to unsigned long in order
to format it with fprintf, in a conforming hosted implementation
prior to C99 (which introduced a conversion specifier guaranteed to
be suitable for size_t).

Casting a plain char or explicitly signed char variable to unsigned
char for use with the is... and to... "functions" defined in ctype.h,
which may be implemented as macros and may invoke UB if called with a
negative value.

I agree that casts should be rare in most well-written C code.

--
Michael Wojcik (e-mail address removed)

Unlikely predition o' the day:
Eventually, every programmer will have to write a Java or distributed
object program.
-- Orfali and Harkey, _Client / Server Programming with Java and CORBA_
 
R

Richard Bos

E. Robert Tisdale said:
The term "undefined behavior" means exactly that.
The ISO C standards do *not* specify the behavior
of pointers to objects outside of array bounds
so they are allowed to "work" and, in fact, they do work
for every compiler that complies with ISO C99.

That is so blatant a contradiction that I won't even start trying to
explain where you're going wrong. If you don't see that "It needn't
work, so it works everywhere" is idiotic, there's no curing you.

Richard
 
M

Mark McIntyre

Casting an object pointer of another type to unsigned char * in order
to inspect an object's representation, or to copy it to another area
of memory, and so forth.

Grin. All accepted.

I was thinking more of the context of Old Wolf's post, which as far as I
recall was suggesting that the casting of malloc was sometimes appropriate.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,772
Messages
2,569,592
Members
45,104
Latest member
LesliVqm09
Top