This is an unfortunate example for instruction, because it implies it's okay to do some incorrect things that often work in practice.
The technically correct answer is that the program has Undefined Behavior, so any result is possible, including printing -10, printing a different number, printing something different or nothing at all, failing to run, crashing, and/or doing something entirely unrelated.
The undefined behavior comes up from evaluating the subexpression array -2
. array
decays from its array type to a pointer to the first element. array -2
would point at the element which comes two positions before that, but there is no such element (and it's not the "one-past-the-end" special rule), so evaluating that is a problem no matter what context it appears in.
(C11 6.5.6/8 says)
When an expression that has integer type is added to or subtracted from a pointer, .... If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
Now the technically incorrect answer the instructor is probably looking for is what actually happens on most implementations:
Even though array -2
is outside the actual array, it evaluates to some address which is 2*sizeof(int)
bytes before the address where the array's data starts. It's invalid to dereference that address since we don't know that there actually is any int
there, but we're not going to.
Looking at the larger expression -2[array -2]
, the []
operator has higher precedence than the unary -
operator, so it means -(2[array -2])
and not (-2)[array -2]
. A[B]
is defined to mean the same as *((A)+(B))
. It's customary to have A
be a pointer value and B
be an integer value, but it's also legal to use them reversed like we're doing here. So these are equivalent:
-2[array -2]
-(2[array -2])
-(*(2 + (array - 2)))
-(*(array))
The last step acts like we would expect: Adding two to the address value of array - 2
is 2*sizeof(int)
bytes after that value, which gets us back to the address of the first array element. So *(array)
dereferences that address, giving 10, and -(*(array))
negates that value, giving -10. The program prints -10.
You should never count on things like this, even if you observe it "works" on your system and compiler. Since the language guarantees nothing about what will happen, the code might not work if you make slight changes which seem they shouldn't be related, or on a different system, a different compiler, a different version of the same compiler, or using the same system and compiler on a different day.