Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtaining a past-the-end pointer using the address of an array

In C and C++, it is often useful to use a past-the-end pointer to write functions that can operate on arbitrarily large arrays. C++ gives a std::end overload to make this easier. In C, on the other hand, I've found it's not uncommon to see a macro defined and used like this:

#define ARRAYLEN(array) (sizeof(array)/sizeof(array[0]))

// ...

int a [42];
do_something (a, a + ARRAYLEN (a));

I've also seen a pointer arithmetic trick used to let such functions operate on single objects:

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

It occured to me that something similar could be done with arrays, since they are considered by C (and, I believe, C++) to be "complete objects." Given an array, we can derive a pointer to an array immediately after it, dereference that pointer, and use array-to-pointer conversion on the resulting reference to an array to get a past-the-end pointer for the original array:

#define END(array) (*(&array + 1))

// ...

int a [42];
do_something (a, END (a));

My question is this: In dereferencing a pointer to a non-existent array object, does this code exhibit undefined behaviour? I'm interested in what the most recent revisions of both C and C++ have to say about this code (not because I intend to use it, as there are better ways of achieving the same result, but because it's an interesting question).

like image 259
Stuart Olsen Avatar asked Dec 14 '13 01:12

Stuart Olsen


2 Answers

I've used that in my own code, as (&arr)[1].

I'm quite sure it is safe. Array to pointer decay is not "lvalue-to-rvalue conversion", although it starts with an lvalue and ends with an rvalue.

like image 144
Ben Voigt Avatar answered Nov 17 '22 16:11

Ben Voigt


It is undefined behaviour.

a is of type array of 42 int.

&a is of type pointer to array of 42 int. (Note this is not an array-to-pointer conversion)

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

5.7p5 states:

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

The pointer does not point to an element of an array object. It points to an array object. So the "otherwise, the behaviour is undefined" is true. Behaviour is undefined.

like image 45
Andrew Tomazos Avatar answered Nov 17 '22 17:11

Andrew Tomazos