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++;
A NULL pointer does not point at anything, so incrementing it gives 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.
Because a pointer points to an address (which is also a numeric value), we can also increment a pointer. However, we are incrementing by address value instead of integer value.
A null pointer is a pointer pointing to the 0th memory location, which is a reserved memory and cannot be dereferenced. #include <stdio.h> int main()
The difference is number++ returns number and then increments number, and ++number increments first and then returns it. Third, by increasing the value of a pointer, you're incrementing it by the sizeof its contents, that is you're incrementing it as if you were iterating in an array.
§5.2.6/1:
The value of the operand object is modified by adding
1
to it, unless the object is of typebool
[..]
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With