Reputation: 263128
If I don't actually access the dereferenced "object", is dereferencing the null pointer still undefined?
int* p = 0;
int& r = *p; // undefined?
int* q = &*p; // undefined?
A slightly more practical example: can I dereference the null pointer to distinguish between overloads?
void foo(Bar&);
void foo(Baz&);
foo(*(Bar*)0); // undefined?
Okay, the reference examples are definitely undefined behavior according to the standard:
a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the "object" obtained by dereferencing a null pointer, which causes undefined behavior.
Unfortunately, the emphasized part is ambiguous. Is it the binding part that causes undefined behavior, or is the dereferencing part sufficient?
Upvotes: 8
Views: 2113
Reputation: 361472
int& r = *p; // undefined?
I think right here you've undefined behavior even if you don't actually use r
(or *p
)- the dereferenced object. Because after this step (i.e dereferencing the null pointer), the program behaviour is not guaranteed by the language, as the program may crash immediately which is one of the possibilities of UB. You seem to think that only reading the value of r
so as to be used in real purpose invokes UB. I don't think so.
Also, the language specification clearly says "the effect of dereferencing the null pointer" invokes undefined behavior. It does not say "the effect of actually using dereferenced object from a null pointer" invokes UB. The effect of dereferencing the null pointer (or in other words undefined behavior) doesn't mean that you will necessarily and immediately get problems, or it must crash immediately after dereferencing the null pointer. No. It simply means, the entire program behavior is not defined if the program contains any UB at all (like dereferencing a null pointer). That is, the program may run normally, as expected, from start to end. Or it may crash immediately, or after after some time - after few minutes, hours or days. Anything can happen anytime, before or after dereferencing the null pointer.
Upvotes: 6
Reputation: 506995
Yes it is undefined behavior, because the spec says that an "lvalue designates an object or function" (at clause 3.10) and it says for the *
-operator "the result [of dereferencing] is an lvalue referring to the object or function to which the expression points" (at clause 5.3.1).
That means there is no description for what happens when you dereference a null pointer. It's simply undefined behavior.
Upvotes: 4
Reputation: 299880
I think the second opus of What every C programmer should know about Undefined Behavior might help illustrate this issue.
Taking the example of the blog:
void contains_null_check(int *P) {
int dead = *P;
if (P == 0)
return;
*P = 4;
}
Might be optimized to (RNCE: Redundant Null Check Elimintation):
void contains_null_check_after_RNCE(int *P) {
int dead = *P;
if (false) // P was dereferenced by this point, so it can't be null
return;
*P = 4;
}
Which is turn optimized into (DCE: Dead Code Elimination):
void contains_null_check_after_RNCE_and_DCE(int *P) {
//int dead = *P; -- dead store
//if (false) -- unreachable branch
// return;
*P = 4;
}
As you can see, even though dead
is never used, the simple int dead = *P
assignment has caused Undefined Behavior to creep in the program.
To distinguish between overloads, I'd suggest using a pointer (which might be null) rather than artificially creating a null reference and exposing yourself to Undefined Behavior.
Upvotes: 6