Getting a pointer at the end using an array address

In C and C ++, it is often useful to use a pointer to the past to write functions that can work on arbitrarily large arrays. C ++ gives an overload of std::end to make this easier. In C, on the other hand, I often found to see a macro defined and used as follows:

 #define ARRAYLEN(array) (sizeof(array)/sizeof(array[0])) // ... int a [42]; do_something (a, a + ARRAYLEN (a)); 

I also saw the arithmetic pointer trick used to make such functions work on individual objects:

 int b; do_something (&b, &b + 1); 

It seemed to me that something similar could be done with arrays, since they are considered by C (and, in my opinion, C ++) to be "full objects". Given the array, we can get a pointer to the array immediately after it, dereference this pointer and use the conversion of the array to a pointer in the resulting array reference to get a pointer to the end of the original array:

 #define END(array) (*(&array + 1)) // ... int a [42]; do_something (a, END (a)); 

My question is this: When dereferencing a pointer to a nonexistent array object, does this code exhibit undefined behavior? . I am wondering what recent changes both C and C ++ have to say about this code (not because I intend to use it, because there are better ways to achieve the same result, but because it is an interesting question).

+7
c ++ c undefined-behavior c11 c ++ 11
source share
3 answers

I used this in my own code, like (&arr)[1] .

I am sure it is safe. An array for decomposing a pointer is not a "lvalue-to-rvalue conversion", although it starts with an lvalue and ends with an rvalue.

+1
source share

This behavior is undefined.

a is of type array of 42 int .

&a is of type pointer to array of 42 int . (Note that this is not a conversion between arrays and pointers)

&a + 1 also has type pointer to array of 42 int

5.7p5:

When an expression that has an integral type is added or subtracted from the pointer, the result is the type of the operand of the pointer. If the pointer operand points to an element of an array object, and [...] otherwise the behavior is undefined

A pointer does not point to an element of an array object. It points to an array object. So the “otherwise undefined behavior” is true. The behavior is undefined.

+1
source share

This behavior is undefined in C, dereferencing a pointer that points to an existing object always if it itself is not part of a larger object containing more elements.

But the basic idea of ​​using &array + 1 true when array is an lvalue. (There are times when arrays are not lvalues.) In this case, this is a valid pointer operation. Now, to get a pointer to the first element, you just need to drop it back to the base type. In your case it will be

 (int*)(&array + 1) 

The value of the pointer to the array is guaranteed to be the same value as the pointer to its first element, only the types are different.

Unfortunately, I don’t see a way to make this type of expression agnostic so that you can put it in a common macro, unless you click on void* . (With the gcc typeof extension you can do, for example) Therefore, you better stick with a portable (array)+ARRAYLEN(array) , which should work in all cases.

In a strange angular case, an array that is part of a struct and returned as an rvalue from a function is not an lvalue. I think the standard allows pointer arithmetic here too, I never understood this construct completely, so I'm not sure if it will work in this case.

0
source share

All Articles