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
How does the non virtual method say_hi work on a NULL pointer?
Where does the object foo get allocated?
Any thoughts?
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.
The say_hi() member function is usually implemented by the compiler as
void say_hi(Foo *this);
Since you don't access any members, your call succeeds (even though you are entering undefined behaviour according to the standard).
Foo doesn't get allocated at all.
Dereferencing a NULL pointer causes "undefined behaviour", This means that anything could happen - your code may even appear to work correctly. You must not depend on this however - if you run the same code on a different platform (or even possibly on the same platform) it will probably crash.
In your code there is no Foo object, only a pointer which is initalised with the value NULL.
It is undefined behaviour, but most compilers generate instructions which will handle this situation correctly if you access neither member variables nor the virtual table.
Let's see the disassembly generated by Visual Studio to understand what happens:
Foo* foo = 0;
004114BE mov dword ptr [foo],0
foo->say_hi(); // works well
004114C5 mov ecx,dword ptr [foo]
004114C8 call Foo::say_hi (411091h)
foo->say_virtual_hi(); // will crash the app
004114CD mov eax,dword ptr [foo]
004114D0 mov edx,dword ptr [eax]
004114D2 mov esi,esp
004114D4 mov ecx,dword ptr [foo]
004114D7 mov eax,dword ptr [edx]
004114D9 call eax
As you can see Foo:say_hi is called as a normal function, but with this in the ecx register. To simplify you can assume that this is passed as implicit parameter, which is never used in your example.
But in the second case the address of the function must be calculated, due to the virtual table - this requires that the address of foo is valid and causes a crash.
a) It works because it does not dereference anything through the implicit "this" pointer. As soon as you do that, boom. I'm not 100% sure, but I think null pointer dereferences are done by RW protecting first 1K of memory space, so there is a small chance of nullreferencing not getting caught if you only dereference it past 1K line (ie. some instance variable that would get allocated very far, like:
class A {
char foo[2048];
int i;
}
then a->i would possibly be uncaught when A is null.
b) Nowhere, you only declared a pointer, which is allocated on main():s stack.
It's important to realize that both calls produce undefined behavior, and that behavior may manifest in unexpected ways. Even if the call appears to work, it may be laying down a minefield.
Consider this small change to your example:
Foo* foo = 0;
foo->say_hi(); // appears to work
if (foo != 0)
foo->say_virtual_hi(); // why does it still crash?
Since the first call to foo enables undefined behavior if foo is null, the compiler is now free to assume that foo is not null. That makes the if (foo != 0) redundant, and the compiler can optimize it out! You might think this is a very senseless optimization, but the compiler writers have been getting very aggressive, and something like this has happened in actual code.
The call to say_hi is statically bound. So the computer actually simply does a standard call to a function. The function doesn't use any fields, so there is no problem.
The call to virtual_say_hi is dynamically bound, so the processor goes to the virtual table, and since there is no virtual table there, it jumps somewhere random and crashes the program.
In the original days of C++, the C++ code was converted to C. Object methods are converted to non-object methods like this (in your case):
foo_say_hi(Foo* thisPtr, /* other args */)
{
}
Of course, the name foo_say_hi is simplified. For more details, look up C++ name mangling.
As you can see, if the thisPtr is never dereferenced, then the code is fine and succeeds. In your case, no instance variables or anything that depends on the thisPtr was used.
However, virtual functions are different. There's a lot of object lookups to make sure the right object pointer is passed as the paramter to the function. This will dereference the thisPtr and cause the exception.
Related
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
How does the non virtual method say_hi work on a NULL pointer?
Where does the object foo get allocated?
Any thoughts?
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.
The say_hi() member function is usually implemented by the compiler as
void say_hi(Foo *this);
Since you don't access any members, your call succeeds (even though you are entering undefined behaviour according to the standard).
Foo doesn't get allocated at all.
Dereferencing a NULL pointer causes "undefined behaviour", This means that anything could happen - your code may even appear to work correctly. You must not depend on this however - if you run the same code on a different platform (or even possibly on the same platform) it will probably crash.
In your code there is no Foo object, only a pointer which is initalised with the value NULL.
It is undefined behaviour, but most compilers generate instructions which will handle this situation correctly if you access neither member variables nor the virtual table.
Let's see the disassembly generated by Visual Studio to understand what happens:
Foo* foo = 0;
004114BE mov dword ptr [foo],0
foo->say_hi(); // works well
004114C5 mov ecx,dword ptr [foo]
004114C8 call Foo::say_hi (411091h)
foo->say_virtual_hi(); // will crash the app
004114CD mov eax,dword ptr [foo]
004114D0 mov edx,dword ptr [eax]
004114D2 mov esi,esp
004114D4 mov ecx,dword ptr [foo]
004114D7 mov eax,dword ptr [edx]
004114D9 call eax
As you can see Foo:say_hi is called as a normal function, but with this in the ecx register. To simplify you can assume that this is passed as implicit parameter, which is never used in your example.
But in the second case the address of the function must be calculated, due to the virtual table - this requires that the address of foo is valid and causes a crash.
a) It works because it does not dereference anything through the implicit "this" pointer. As soon as you do that, boom. I'm not 100% sure, but I think null pointer dereferences are done by RW protecting first 1K of memory space, so there is a small chance of nullreferencing not getting caught if you only dereference it past 1K line (ie. some instance variable that would get allocated very far, like:
class A {
char foo[2048];
int i;
}
then a->i would possibly be uncaught when A is null.
b) Nowhere, you only declared a pointer, which is allocated on main():s stack.
It's important to realize that both calls produce undefined behavior, and that behavior may manifest in unexpected ways. Even if the call appears to work, it may be laying down a minefield.
Consider this small change to your example:
Foo* foo = 0;
foo->say_hi(); // appears to work
if (foo != 0)
foo->say_virtual_hi(); // why does it still crash?
Since the first call to foo enables undefined behavior if foo is null, the compiler is now free to assume that foo is not null. That makes the if (foo != 0) redundant, and the compiler can optimize it out! You might think this is a very senseless optimization, but the compiler writers have been getting very aggressive, and something like this has happened in actual code.
The call to say_hi is statically bound. So the computer actually simply does a standard call to a function. The function doesn't use any fields, so there is no problem.
The call to virtual_say_hi is dynamically bound, so the processor goes to the virtual table, and since there is no virtual table there, it jumps somewhere random and crashes the program.
In the original days of C++, the C++ code was converted to C. Object methods are converted to non-object methods like this (in your case):
foo_say_hi(Foo* thisPtr, /* other args */)
{
}
Of course, the name foo_say_hi is simplified. For more details, look up C++ name mangling.
As you can see, if the thisPtr is never dereferenced, then the code is fine and succeeds. In your case, no instance variables or anything that depends on the thisPtr was used.
However, virtual functions are different. There's a lot of object lookups to make sure the right object pointer is passed as the paramter to the function. This will dereference the thisPtr and cause the exception.
Amazingly people may call it feature but I use to say it another bug of C++ that we can call member function through pointer without assigning any object. See following example:
class A{
public:
virtual void f1(){cout<<"f1\n";}
void f2(){cout<<"f2\n";};
};
int main(){
A *p=0;
p->f2();
return 0;
}
Output:
f2
We have checked this in different compilers & platforms but result is same, however if we call virtual function through pointer without object then there occur run-time error. Here reason is obvious for virtual function when object is checked it is not found so there comes error.
This is not a bug. You triggered an Undefined Behavior. You may get anything including the result you expected.
Dereferencing a NULL pointer is undefined behavior.
BTW, there is no thing such as "bug of C++". The bugs may occur in C++ Compilers not in the language it self.
As pointed out, this is undefined behaviour, so anything goes.
To answer the question in terms of the implementation, why do you see this behaviour?
The non-virtual call is implemented as just an ordinary function call, with the this pointer (value null) passed in as paremeter. The parameter is not dereferenced (as no member variables are used), so the call succeeds.
The virtual call requires a lookup in the vtable to get the adress of the actual function to call. The vtable address is stored in a pointer in the data of the object itself. Thus to read it, a de-reference of the this pointer is required - segmentation fault.
When you create a class by
class A{
public:
virtual void f1(){cout<<"f1\n";}
void f2(){cout<<"f2\n";};
};
The Compiler puts the code of member functions in the text area.
When you do p->MemberFunction() then the compiler just deferences p and tries to find the function MemberFunction using the type information of p which is Class A.
Now since the function's code exists in the text area so it is called. If the function had references to some class variables then while accessing them, you might have gotten a Segmentation Fault as there is no object, but since that is not the case, hence the function executes properly.
NOTE: It all depends on how a compiler implements member function access. Some compiler may choose to see if the pointer of object is null before accessing the member function, but then the pointer may have some garbage value instead of 0 which a compiler cannot check, so generally compilers ignore this check.
You can achieve a lot with undefined behavior. You can even call a function which only takes 1 argument and receive the second one like this:
#include <iostream>
void Func(int x)
{
uintptr_t ptr = reinterpret_cast<uintptr_t>(&x) + sizeof(x);
uintptr_t* sPtr = (uintptr_t*)ptr;
const char* secondArgument = (const char*)*sPtr;
std::cout << secondArgument << std::endl;
}
int main()
{
typedef void(*PROCADDR)(int, const char*);
PROCADDR ext_addr = reinterpret_cast<PROCADDR>(&Func);
//call the function
ext_addr(10, "arg");
return 0;
}
Compile and run under windows and you will get "arg" as result for the second argument. This is not a fault within C++, it is just plain stupid on my part :)
This will work on most compilers. When you make a call to a method (non virtual), the compiler translates:
obj.foo();
to something:
foo(&obj);
Where &obj becomes the this pointer for foo method. When you use a pointer:
Obj *pObj = NULL;
pObj->foo();
For the compiler it is nothing but:
foo(pObj);
i.e.:
foo(NULL);
Calling any function with null pointer is not a crime, the null pointer (i.e. pointer having null value) will be pushed to call stack. It is up to the target function to check if null was passed to it. It is like calling:
strlen(NULL);
Which will compile, and also run, if it is handled:
size_t strlen(const char* ptr) {
if (ptr==NULL) return 0;
... // rest of code if `ptr` is not null
}
Thus, this is very much valid:
((A*)NULL)->f2();
As long as f2 is non-virtual, and if f2 doesn't read/write anything out of this, including any virtual function calls. Static data and function access will still be okay.
However, if method is virtual, the function call is not as simple as it appears. Compiler puts some additional code to perform late binding of given function. The late binding is totally based on what is being pointed by this pointer. It is compiler dependent, but a call like:
obj->virtual_fun();
Will involve looking up the current type of obj by virtual function table lookup. therefore, obj must not be null.
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
How does the non virtual method say_hi work on a NULL pointer?
Where does the object foo get allocated?
Any thoughts?
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.
The say_hi() member function is usually implemented by the compiler as
void say_hi(Foo *this);
Since you don't access any members, your call succeeds (even though you are entering undefined behaviour according to the standard).
Foo doesn't get allocated at all.
Dereferencing a NULL pointer causes "undefined behaviour", This means that anything could happen - your code may even appear to work correctly. You must not depend on this however - if you run the same code on a different platform (or even possibly on the same platform) it will probably crash.
In your code there is no Foo object, only a pointer which is initalised with the value NULL.
It is undefined behaviour, but most compilers generate instructions which will handle this situation correctly if you access neither member variables nor the virtual table.
Let's see the disassembly generated by Visual Studio to understand what happens:
Foo* foo = 0;
004114BE mov dword ptr [foo],0
foo->say_hi(); // works well
004114C5 mov ecx,dword ptr [foo]
004114C8 call Foo::say_hi (411091h)
foo->say_virtual_hi(); // will crash the app
004114CD mov eax,dword ptr [foo]
004114D0 mov edx,dword ptr [eax]
004114D2 mov esi,esp
004114D4 mov ecx,dword ptr [foo]
004114D7 mov eax,dword ptr [edx]
004114D9 call eax
As you can see Foo:say_hi is called as a normal function, but with this in the ecx register. To simplify you can assume that this is passed as implicit parameter, which is never used in your example.
But in the second case the address of the function must be calculated, due to the virtual table - this requires that the address of foo is valid and causes a crash.
a) It works because it does not dereference anything through the implicit "this" pointer. As soon as you do that, boom. I'm not 100% sure, but I think null pointer dereferences are done by RW protecting first 1K of memory space, so there is a small chance of nullreferencing not getting caught if you only dereference it past 1K line (ie. some instance variable that would get allocated very far, like:
class A {
char foo[2048];
int i;
}
then a->i would possibly be uncaught when A is null.
b) Nowhere, you only declared a pointer, which is allocated on main():s stack.
It's important to realize that both calls produce undefined behavior, and that behavior may manifest in unexpected ways. Even if the call appears to work, it may be laying down a minefield.
Consider this small change to your example:
Foo* foo = 0;
foo->say_hi(); // appears to work
if (foo != 0)
foo->say_virtual_hi(); // why does it still crash?
Since the first call to foo enables undefined behavior if foo is null, the compiler is now free to assume that foo is not null. That makes the if (foo != 0) redundant, and the compiler can optimize it out! You might think this is a very senseless optimization, but the compiler writers have been getting very aggressive, and something like this has happened in actual code.
The call to say_hi is statically bound. So the computer actually simply does a standard call to a function. The function doesn't use any fields, so there is no problem.
The call to virtual_say_hi is dynamically bound, so the processor goes to the virtual table, and since there is no virtual table there, it jumps somewhere random and crashes the program.
In the original days of C++, the C++ code was converted to C. Object methods are converted to non-object methods like this (in your case):
foo_say_hi(Foo* thisPtr, /* other args */)
{
}
Of course, the name foo_say_hi is simplified. For more details, look up C++ name mangling.
As you can see, if the thisPtr is never dereferenced, then the code is fine and succeeds. In your case, no instance variables or anything that depends on the thisPtr was used.
However, virtual functions are different. There's a lot of object lookups to make sure the right object pointer is passed as the paramter to the function. This will dereference the thisPtr and cause the exception.
i want to see the content of the vtable of the class A, especially the virtual desctructor, but i can not call it through a function pointer.
Here is my code:
typedef void (*fun)();
class A {
public:
virtual func() {printf("A::func() is called\n");}
virtual ~A() {printf("A::~A() is called\n");}
};
//enter in the vtable
void *getvtable (void* p, int off){
return (void*)*((unsigned int*)p+off);
}
//off_obj is used for multiple inherence(so not here), off_vtable is used to specify the position of function in vtable
fun getfun (A* obj, unsigned int off_obj,int off_vtable){
void *vptr = getvtable(obj,off_obj);
unsigned char *p = (unsigned char *)vptr;
p += sizeof(void*) * off_vtable;
return (fun)getvtable(p,0);
}
void main() {
A* ptr_a = new A;
fun pfunc = getfun(ptr_a,0,0);
(*pfunc)();
pfunc = getfun(ptr_a,0,1);
(*pfunc)(); //error occurred here, this is supposed to be the virtual desctrutor, why?
}
Let's suppose for the sake of argument that the vtable in question really is laid out the way you think it is, as a table of ordinary memory addresses, and that when casting those addresses to function pointers, they're callable.
You have at least two problems:
The calling convention for the member functions isn't necessarily the same as for ordinary functions. Microsoft's default calling convention is thiscall, which places a pointer to the object whose method is being called in the ECX register. There's no facility for specifying that manually; the only way to make that happen is by calling a member function in the way member functions are called, which involves syntax like obj.f() or pobj->f(). You can't do that with pointers to functions (not even member-function pointers), unless you write machine code or assembler to get all the low-level details right.
You happen not to hit this problem for func because it doesn't make reference to this (either directly or by implicit reference to other members). The destructor does, though. Destructors are special, and what's actually stored in the vtable is a pointer to a compiler-generated helper function that calls the real destructor and then checks some flags passed as a hidden parameter to determine whether it should free the object's memory. The value that happens to be in ECX doesn't matter for the func call, but it's very important to be right for the ~A call.
Destructors aren't like normal functions. As I mentioned above, the compiler can generate one or more helper functions, and they receive parameters in addition to this. You haven't accounted for that in your code. The compiler generates separate helpers for array and non-array destructors, so right now we don't even know which one you found at index 1 of the vtable. But since you didn't pass it a valid flag parameter, and there's no way to pass it the this value, it doesn't really matter what you find in the vtable anyway.
You can attempt to solve the first problem by specifying a different calling convention, like stdcall. That puts the this parameter back on the stack with the rest of the parameters, and that allows you to pass it when you call the function pointer. For func, fun would need to have a declaration like this:
typedef void (__stdcall * fun)(A*);
Invoke pfunc like this:
pfunc(ptr_a);
To solve the second problem, you'll need to determine the actual order of the vtable functions so you know to find the right destructor helper. And to call it, you'd need a different function-pointer declaration, too. Destructors don't technically have a return type, but void works well enough. You could use something like this:
typedef void (__stdcall * destr)(A*, unsigned flags);
For most of this answer, I've used an article by Igorsk about recognizing certain patterns in a program for the purpose of decompiling it back into C++. Part 2 covers classes.
You don't call the destructor. You call operator delete(), and it figures out the destructor. Calling destructors directly is Undefined Behavior, in the same sense that dereferencing NULL is, i.e. blows up on every platform I've seen.
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
How does the non virtual method say_hi work on a NULL pointer?
Where does the object foo get allocated?
Any thoughts?
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.
The say_hi() member function is usually implemented by the compiler as
void say_hi(Foo *this);
Since you don't access any members, your call succeeds (even though you are entering undefined behaviour according to the standard).
Foo doesn't get allocated at all.
Dereferencing a NULL pointer causes "undefined behaviour", This means that anything could happen - your code may even appear to work correctly. You must not depend on this however - if you run the same code on a different platform (or even possibly on the same platform) it will probably crash.
In your code there is no Foo object, only a pointer which is initalised with the value NULL.
It is undefined behaviour, but most compilers generate instructions which will handle this situation correctly if you access neither member variables nor the virtual table.
Let's see the disassembly generated by Visual Studio to understand what happens:
Foo* foo = 0;
004114BE mov dword ptr [foo],0
foo->say_hi(); // works well
004114C5 mov ecx,dword ptr [foo]
004114C8 call Foo::say_hi (411091h)
foo->say_virtual_hi(); // will crash the app
004114CD mov eax,dword ptr [foo]
004114D0 mov edx,dword ptr [eax]
004114D2 mov esi,esp
004114D4 mov ecx,dword ptr [foo]
004114D7 mov eax,dword ptr [edx]
004114D9 call eax
As you can see Foo:say_hi is called as a normal function, but with this in the ecx register. To simplify you can assume that this is passed as implicit parameter, which is never used in your example.
But in the second case the address of the function must be calculated, due to the virtual table - this requires that the address of foo is valid and causes a crash.
a) It works because it does not dereference anything through the implicit "this" pointer. As soon as you do that, boom. I'm not 100% sure, but I think null pointer dereferences are done by RW protecting first 1K of memory space, so there is a small chance of nullreferencing not getting caught if you only dereference it past 1K line (ie. some instance variable that would get allocated very far, like:
class A {
char foo[2048];
int i;
}
then a->i would possibly be uncaught when A is null.
b) Nowhere, you only declared a pointer, which is allocated on main():s stack.
It's important to realize that both calls produce undefined behavior, and that behavior may manifest in unexpected ways. Even if the call appears to work, it may be laying down a minefield.
Consider this small change to your example:
Foo* foo = 0;
foo->say_hi(); // appears to work
if (foo != 0)
foo->say_virtual_hi(); // why does it still crash?
Since the first call to foo enables undefined behavior if foo is null, the compiler is now free to assume that foo is not null. That makes the if (foo != 0) redundant, and the compiler can optimize it out! You might think this is a very senseless optimization, but the compiler writers have been getting very aggressive, and something like this has happened in actual code.
The call to say_hi is statically bound. So the computer actually simply does a standard call to a function. The function doesn't use any fields, so there is no problem.
The call to virtual_say_hi is dynamically bound, so the processor goes to the virtual table, and since there is no virtual table there, it jumps somewhere random and crashes the program.
In the original days of C++, the C++ code was converted to C. Object methods are converted to non-object methods like this (in your case):
foo_say_hi(Foo* thisPtr, /* other args */)
{
}
Of course, the name foo_say_hi is simplified. For more details, look up C++ name mangling.
As you can see, if the thisPtr is never dereferenced, then the code is fine and succeeds. In your case, no instance variables or anything that depends on the thisPtr was used.
However, virtual functions are different. There's a lot of object lookups to make sure the right object pointer is passed as the paramter to the function. This will dereference the thisPtr and cause the exception.