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, dere
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 a NULL
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 the this
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 the vtable
from the NULL
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.
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.
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:
void anotherfunc(anotherObj* this, someObj& arg);
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.
It would still cause a crash because you're still instructing the compiler to attempt to access the memory at location 0 (which is forbidden). Depending on the signature of anotherfunc
, you may be passing a reference (which are forbidden from being initialized with a NULL object), or a copy of *b
.
You are wandering in undefined territories.
You can think of calling a member function like calling a regular function with the additional, implicit this
pointer argument. The function call itself is just putting the arguments in place according to call convention and jumping to a memory address.
So just calling a member function on a NULL object pointer does not necassarily cause a crash (unless it is a virtual function). You get invalid memory access crashes only when you try to access the object's member variables or vtable.
In case #2 you may or may not get an immediate crash, depending on how anotherfunc
is declared. If it takes someObj
by value, then you're indirecting NULL in the function call itself, resulting in a crash. If it takes someObj
by reference, usually nothing happens since references are implemented using pointers under the hood and the actual indirection is postponed until you try to access member data.
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)