Is incrementing a null pointer well-defined?

There are lots of examples of undefined/unspecified behavior when doing pointer arithmetics - pointers have to point inside the same array (or one past the end), or inside the same object, restrictions on when you can do comparisons/operations based on the above, etc.

Is the following operation well-defined?

int* p = 0;
p++;

Solution 1:

§5.2.6/1:

The value of the operand object is modified by adding 1 to it, unless the object is of type bool [..]

And additive expressions involving pointers are defined in §5.7/5:

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.

Solution 2:

There seems to be quite low understanding what "undefined behaviour" means.

In C, C++, and related languages like Objective-C, there are four kinds of behaviour: There is behaviour defined by the language standard. There is implementation defined behaviour, which means the language standard explicitely says that the implementation must define the behaviour. There is unspecified behaviour, where the language standard says that several behaviours are possible. And there is undefined behaviour, where the language standard doesn't say anything about the result. Because the language standard doesn't say anything about the result, anything at all can happen with undefined behaviour.

Some people here assume that "undefined behaviour" means "something bad happens". That's wrong. It means "anything can happen", and that includes "something bad can happen", not "something bad must happen". In practice it means "nothing bad happens when you test your program, but as soon as it is shipped to a customer, all hell breaks loose". Since anything can happen, the compiler can actually assume that there is no undefined behaviour in your code - because either it is true, or it is false, in which case anything can happen, which means whatever happens because of the compiler's wrong assumption is still correct.

Someone claimed that when p points to an array of 3 elements, and p + 4 is calculated, nothing bad will happen. Wrong. Here comes your optimising compiler. Say this is your code:

int f (int x)
{
    int a [3], b [4];
    int* p = (x == 0 ? &a [0] : &b [0]);
    p + 4;
    return x == 0 ? 0 : 1000000 / x;
}

Evaluating p + 4 is undefined behaviour if p points to a [0], but not if it points to b [0]. The compiler is therefore allowed to assume that p points to b [0]. The compiler is therefore allowed to assume that x != 0, because x == 0 leads to undefined behaviour. The compiler is therefore allowed to remove the x == 0 check in the return statement and just return 1000000 / x. Which means your program crashes when you call f (0) instead of returning 0.

Another assumption made was that if you increment a null pointer and then decrement it again, the result is again a null pointer. Wrong again. Apart from the possibility that incrementing a null pointer might just crash on some hardware, what about this: Since incrementing a null pointer is undefined behavour, the compiler checks whether a pointer is null and only increments the pointer if it isn't a null pointer, so p + 1 is again a null pointer. And normally it would do the same for the decrementing, but being a clever compiler it notices that p + 1 is always undefined behaviour if the result was a null pointer, therefore it can be assumed that p + 1 isn't a null pointer, therefore the null pointer check can be ommitted. Which means (p + 1) - 1 is not a null pointer if p was a null pointer.

Solution 3:

Operations on a pointer (like incrementing, adding, etc) are generally only valid if both the initial value of the pointer and the result point to elements of the same array (or to one past the last element). Otherwise the result is undefined. There are various clauses in the standard for the various operators saying this, including for incrementing and adding.

(There are a couple of exceptions like adding zero to NULL or subtracting zero from NULL being valid, but that doesn't apply here).

A NULL pointer does not point at anything, so incrementing it gives undefined behaviour (the "otherwise" clause applies).