Preamable
Quite a few of the answers here are fairly old, and quote relatively old versions of the C++ standard (or drafts thereof). Others are based on the C standard; C99 was revised specifically to make this legal, with defined behavior, but that doesn't mean a matching change was made in C++. It looks like the text in the C++ standard has changed somewhat over time, so it may be unclear how meaningful some of the older citations are for C++ as currently defined.
Since the wording has changed over time, I'm going to cite a couple of specific drafts of the C++ standard. If later drafts revise the wording again (which wouldn't surprise me) the issue would have to be analyzed again with respect to the revised wording.
N4835
A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions
shall be a glvalue of type “array of T
” or a prvalue of type “pointer to T
” and the other shall be a prvalue of
unscoped enumeration or integral type. The result is of type “T
”. The type “T
” shall be a completely-defined
object type.59 The expression E1[E2]
is identical (by definition) to *((E1)+(E2))
, except that in the case of
an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise. The expression
E1
is sequenced before the expression E2
.
So, array[5]
is equivalent to *(array + 5)
.
We then attempt to take the address of that expression using the &
operator. This is defined as follows (§[expr.unary.op]/3):
The result of the unary &
operator is a pointer to its operand.
- If the operand is a qualified-id naming a non-static or variant member
m
of some class C
with type T
,
the result has type “pointer to member of class C
of type T
” and is a prvalue designating C::m
.
- Otherwise, if the operand is an lvalue of type
T
, the resulting expression is a prvalue of type “pointer to
T
” whose result is a pointer to the designated object (6.7.1) or function. [Note: In particular, taking
the address of a variable of type “cv T
” yields a pointer of type “pointer to cv T
”. —end note] For
purposes of pointer arithmetic (7.6.6) and comparison (7.6.9, 7.6.10), an object that is not an array
element whose address is taken in this way is considered to belong to an array with one element of type
T
.
- Otherwise, the program is ill-formed.
The first of these three possibilities applies to class members, so it's irrelevant here.
The second applies to an lvalue. So the question is whether array + 5
is an lvalue or not. According to §[basic.lval]/1.1:
- A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
[...]
- An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near
the end of its lifetime).
[...]
- An lvalue is a glvalue that is not an xvalue.
While we can form an address one past the end of an array, that address does not determine the identity of an object, bit-field or function. The relevant option would be "object", but there is no object there whose identity it can determine1. As such, when array
has been defined with N elements, array + N
is not an lvalue.
That leaves only the third option: the program is ill-formed.
N4944
N4944 has identical wording for §[expr.sub]/1 as N4835, so I won't quote it again here.
In N4944 the wording with respect to the *
operator has changed slightly. It starts with (§[expr.unary.op]/3):
The operand of the unary & operator shall be an lvalue of some type T
.
N4944 retains the same definition of an lvalue though:
- A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
[...]
- An xvalue is a glvalue that denotes an object whose resources can be reused (usually because it is near
the end of its lifetime).
[...]
- An lvalue is a glvalue that is not an xvalue.
As such, again, a pointer to one past the end of an array is not an lvalue, so code that attempts to apply the *
operator to it is ill-formed.
Conclusion
In recent versions of the C++ standard, code like:
int array[5];
int *foo = &array[5];
...is ill formed.
1. Well, it could happen that there's some object at that address, but if so it's an accidental coincidence. Nothing on the standard requires there to be an object that address.