Delta_G:
I was under the impression that these two would produce the same code after compilation. I must be wrong.
They are the same.
Section J.2 also lists these undefined behaviors:
— Addition or subtraction of a pointer into, or just beyond, an array object and an
integer type produces a result that does not point into, or just beyond, the same array
object (6.5.6).
— Addition or subtraction of a pointer into, or just beyond, an array object and an
integer type produces a result that points just beyond the array object and is used as
the operand of a unary * operator that is evaluated (6.5.6).
The first bullet means that the following code is illegal, and the compiler can do absolutely anything with it:
int array[5];
int *p = array + 6;
Yes, even the simple act of calculating a pointer to beyond the array object (except for "just beyond" the array) is undefined behavior.
In the context of the C99 standard, "just beyond" means one element past the end of the array (such as "array + 5" with an array of 5 elements). It's legal to calculate a pointer to that element, but it's not legal to dereference that pointer (see the second bullet).
Most of the time reading from beyond an array will just give you data from beyond the array (such as a function's return address or some other variable), but in some situations a compiler will outsmart you and completely change the meaning of the program and give you results that you didn't expect (such as turn a finite loop into an infinite loop). That's why undefined behavior is so difficult to detect--a program can appear to "work" one time and then suddenly stop working when you change other code, use a different compiler, update the compiler, use different compiler options, etc.
Slightly OT: I once did something like this:
struct node {
struct node *next;
// other data members omitted for clarity
};
struct node *x = 0;
struct node *y = (struct node *)&x;
y->next = malloc(sizeof *y->next);
It's dodgy, but it seemed to work because "y->next" should be the same as "*x". Then I had to port the software to a different platform (from ColdFire to Arm) and a newer version of GCC. My code broke because it depended on undefined behavior, and either the platform change or the compiler change exposed it.