I was experimenting with C++ and found the below code as very strange.
class Foo{ public: virtual void say_virtual_hi(){ std::cout << "Virtual Hi"; } void say_hi() { std::cout << "Hi"; } }; int main(int argc, char** argv) { Foo* foo = 0; foo->say_hi(); // works well foo->say_virtual_hi(); // will crash the app return 0; }
I know that the virtual method call crashes because it requires a vtable lookup and can only work with valid objects.
I have the following questions
say_hi
work on a NULL pointer?foo
get allocated?Any thoughts?
Declare a pointer p of the integer datatype. Initialize *p= NULL. Print “The value of pointer is”. Print the value of the pointer p.
Because a null pointer does not point to a meaningful object, an attempt to dereference (i.e., access the data stored at that memory location) a null pointer usually (but not always) causes a run-time error or immediate program crash.
But by convention, if a pointer contains the null (zero) value, it is assumed to point to nothing. Thus, if all unused pointers are given the null value and you avoid the use of a null pointer, you can avoid the accidental misuse of an uninitialized pointer.
Initializing a pointer to a function, or a pointer in general to NULL helps some developers to make sure their pointer is uninitialized and not equal to a random value, thereby preventing them of dereferencing it by accident.
The object foo
is a local variable with type Foo*
. That variable likely gets allocated on the stack for the main
function, just like any other local variable. But the value stored in foo
is a null pointer. It doesn't point anywhere. There is no instance of type Foo
represented anywhere.
To call a virtual function, the caller needs to know which object the function is being called on. That's because the object itself is what tells which function should really be called. (That's frequently implemented by giving the object a pointer to a vtable, a list of function-pointers, and the caller just knows it's supposed to call the first function on the list, without knowing in advance where that pointer points.)
But to call a non-virtual function, the caller doesn't need to know all that. The compiler knows exactly which function will get called, so it can generate a CALL
machine-code instruction to go directly to the desired function. It simply passes a pointer to the object the function was called on as a hidden parameter to the function. In other words, the compiler translates your function call into this:
void Foo_say_hi(Foo* this); Foo_say_hi(foo);
Now, since the implementation of that function never makes reference to any members of the object pointed to by its this
argument, you effectively dodge the bullet of dereferencing a null pointer because you never dereference one.
Formally, calling any function — even a non-virtual one — on a null pointer is undefined behavior. One of the allowed results of undefined behavior is that your code appears to run exactly as you intended. You shouldn't rely on that, although you will sometimes find libraries from your compiler vendor that do rely on that. But the compiler vendor has the advantage of being able to add further definition to what would otherwise be undefined behavior. Don't do it yourself.
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