Do you think we can reach any kind of consensus on whether the
following code's behaviour is undefined by the Standard?
int my_array[5];
int const *const pend = *(&my_array + 1);
You may have a pointer one element past the last element of an array
object. However, my_array as whole is not an element of an array. So
&myarray + 1 is invalid.
What you are doing is similar to computing p below:
int i, j[1];
int *p = &i + 1; // not right, i is not an array object
int *q = &j + 1; // okay, since j is an array object
We can fix this in your example, similarly to the trick with j above:
use a one-element array.
But the dereference conundrum is still there:
int my_array[1][5];
int *p = my_array[1];
The problem is clearer now: you're trying to create pointer-based
access to an nonexistent array. The expression my_array[0] refers to a
valid array element, which is an array of 5 ints. But there is no such
array as my_array[1]. This my_array[1] expression has the /type/
``array of 5 int'', but it's not an object. You're allowed to point to
it as a unit, but that's it.
We can show the problem in these two steps:
int my_array[1][5];
int (*q)[5] = my_array + 1;
Now q is a ``pointer to an array of 5 int'', correctly aimed one-
element past the end of an array object. So far so good.
What we're trying to do next is effectively the same as:
int *p = q[0];
We've been given a finger, and want to take the hand. Not happy with
having a pointer one element past the end of an array object, we want
a pointer to the first element of that nonexistent element.
In fact the pointer we're trying to compute points to the same
location as &my_array[0][5], which is allowed, and has the same type.
One element past the end of my_array[0] would appear to be the same
nonexistent thing as the first element of my_array[1] (indeed it has
the same type and address) but the semantics is subtly different.
But if q[0] is okay, why not &q[0][0]. If decay cancels out bad
dereferencing, then address-of can also cancel out more bad
dereferencing. And now you open the door to &q[0][1]. If we can point
to the first element of a nonexistent array of 5 int, why not the
second? It's because we know that the justification for the first
element is that it's really one element past the end of something.
However, we didn't arrive at it that way.
/How/ we arrive at a value can determine whether or not it is correct,
not just the final value itself. If I have two int objects i, and j,
and perform arithmetic on &i so that the result points to j, that's
not correct, even though the result is indistinguishable from the
correct value &j.
Fact is, a bounds checking compiler could be designed to enforce the
semantic rule that dereferencing an out-of-bounds pointer is not
allowed under any circumstances, and consequently that array-to-
pointer decay can only happen over a valid array object.
Considering the syntax of the language, then we definitely do
dereference an invalid pointer... but if we consider the mechanics of the
language, then we know that nothing "happens" when we dereference a pointer
to an array, because arrays are dealt with in terms of pointers.
We could also argue that ``nothing'' happens when you merely increment
a pointer out of bounds.