I am curious as to what part of the dereferencing a NULL ptr causes undesired behavior. Example:
// #1
someObj * a;
a = NULL;
(*a).somefunc(); // crash, dereferenced a null ptr and called one of its function
// same as a->somefunc();
// #2
someObj * b;
anotherObj * c;
b = NULL;
c->anotherfunc(*b); // dereferenced the ptr, but didn't call one of it's functions
Here we see in #2 that I didn't actually try to access data or a function out of b, so would this still cause undesired behavior if *b just resolves to NULL and we're passing NULL into anotherfunc() ?
Reading from or writing to the invalid memory location causes a crash.
A call to a member function through an invalid object pointer will usually succeed, if the method is not virtual and the method does not access any members of the object, since this involves no reads or writes related to the object pointer.
(This is not guaranteed by the standard, even though it work that way on all compilers i ever encountered)
Dereferencing a NULL pointer is undefined behavior.
It is not guaranteed to crash, and you are not guaranteed anything when doing it. For all you know someone somewhere in the world will be punched each time you do it. That is valid behavior since it is undefined.
Also your pointers may not be initialized to NULL so if you want them to be for sure NULL you should set them explicitly to NULL.
Although in the standards dereferencing a zero pointer (NULL) is undefined behavior, current processors and operating systems generate a segmentation fault or similar error.
Maybe that function you called accepts a reference parameter (which IS a pointer) and that function doesn't use the paramenter, so the NULL won't be dereferenced.
In practice, it doesn't crash until it needs to use the
NULL
value. This means that you can call non-virtual functions because they are bound at compile time. It calls the function just fine and passes in aNULL
this
pointer. Now if you try to use any member variables then it will crash because it will try to look them up based on thethis
pointer passed in. You can also call other non-virtual functions by the same argument. Now if you try to use a virtual function it will immediately crash because it tries to find thevtable
from theNULL
pointer.We ran into a case like this and I had to write some example code to demonstrate to the other developers that even though it was reporting the error in 2 levels of calls to member functions it was actually a
NULL
pointer that was being called. The error was manifested when an actual value was used.The second example is also undefined behavior, yes. You are only allowed to call member functions on a valid object. And a null pointer does not point to a valid object.
The reason why it appears to work is that member functions are typically implemented roughly like this:
That is, the "this" pointer is basically passed to the function as a separate argument. So while calling the function, the compiler doesn't check that the
this
pointer is valid, it just passes it to the function.It is still undefined behavior though. The compiler isn't guaranteed to let this work.
There is a concept, in the standard, of a null pointer value. This is a distinct value that causes undefined behavior when the program attempts to access memory through it. In practice, lots of modern implementations have it crash the program, which is useful behavior. After all, such an attempt is a mistake.
The name of the null pointer value is
0
, or any other constant integral expression in pointer context (like3 - 3
, for example). There is also aNULL
macro, which has to evaluate to 0 in C++ but can be(void *)0
in C (C++ insists more on pointers being type-safe). In C++0x, there will be an explicit value callednullptr
, finally giving the null pointer an explicit name.The value of the null pointer doesn't have to be an actual zero, although it is on all implementations I'm aware of, and the odd computers where that didn't work have mostly been retired.
You're misstating what happens in your last example.
*b
doesn't resolve into anything. Passing*b
is undefined behavior, which means the implementation can do anything it likes with it. It may or may not be flagged as an error, and may or may not cause problems. The behavior can change for no apparent reason, and so doing this is a mistake.If a called function is expecting a pointer value, passing it a null pointer value is perfectly legitimate, and the called function should handle it properly. Dereferencing a null pointer value is never legitimate.