16
#include "iostream"
using namespace std;
class A
{
public:
    void mprint()
    {
        cout<<"\n TESTING NULL POINTER";
    }
};

int main()
{
    A *a = NULL;
    a->mprint();
    return 0;
}

I am getting output as "TESTING NULL POINTER". Can anyone please explain why this program is printing the output instead of crashing. I checked it on Dev C++ and aCC compiler both gave same result.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
user258367
  • 3,247
  • 2
  • 18
  • 17
  • 2
    Here's a closely related question http://stackoverflow.com/questions/2533476/what-will-happen-when-i-call-a-member-function-on-a-null-object-pointer – sharptooth Mar 25 '11 at 11:02

6 Answers6

26

You're not using any member variables of A - the function is completely independent of the A instance, and therefore the generated code happens to not contain anything that dereferences 0. This is still undefined behavior - it just may happen to work on some compilers. Undefined behavior means "anything can happen" - including that the program happens to work as the programmer expected.

If you e.g. make mprint virtual you may get a crash - or you may not get one if the compiler sees that it doesn't really need a vtable.

If you add a member variable to A and print this, you will get a crash.

Erik
  • 88,732
  • 13
  • 198
  • 189
  • +1, but ...well, I bet it'll work on almost all compilers - after all, they tend to avoid inserting useless dereferences simply for performance reasons. Still, it's obviously unwise to *rely* on it. – Eamon Nerbonne Mar 25 '11 at 10:54
  • @Eamon Nerbonne: Depends what you mean by almost all compilers. – Martin York Mar 25 '11 at 11:04
  • 3
    @Martin: are you aware of any compiler where it doesn't? Normally when people say "almost all compilers", it means something like, "I can't think of any good reason for a compiler to do otherwise, nor have I seen a compiler do otherwise, but the standard doesn't require it". If there are exceptions to the rule, it may be that simple cases work everywhere, but then some compiler catches you out with an optimization in a more complicated case. For example, put `if (this == 0)` in `mprint`, see if your favourite compiler still prints with optimization on. – Steve Jessop Mar 25 '11 at 11:42
  • @Steve Jessop: Actually I am. I was working for a SOC company (a few years ago) and the chip had 8 address registers. The first 4 normal plain address registers the last 4 started a pre-load of memory into the local cache as soon as they were loaded (the idea being you put the address of an objects in a4-a8 and by the time you accessed a member the data was already in the cache). Normally the `this` pointer is pushed into a4, In this situation you are potentially going BANG! – Martin York Mar 25 '11 at 17:04
  • 1
    @Martin: Excellent, so "they tend to avoid inserting useless dereferences simply for performance reasons" is reversed in that case - it inserts a useless deference to *improve* performance. – Steve Jessop Mar 26 '11 at 12:32
  • @Steve Jessop: That happens a lot on highly optimized hardware and/or specialized compilers. I have also worked on Super computers (KSR-1) with distributed memory systems (ie potentially non local to the physical box) In these systems there were a lot of points the compiler would introduce instructions to pre-load memory before it was needed so that the cache was up to data by the time instructions needing the data executed (different problem to this but another example of needing to get memory into cache before you know what it is). – Martin York Mar 26 '11 at 20:12
7

According to the C++ spec, This program has undefined behavior because you're invoking a member function on a null receiver.

The reason that this works, though, is that non virtual member functions are typically implemented as regular functions that take the "this" pointer as an implicit first argument. Consequently, if you call a member function on a null pointer, as long as you don't use the this pointer, your program will not crash. Of course, you cannot rely n this; a valid C++ compiler could cause this to crash.

However, virtual functions are a different story because the function that actually gets called needs to be resolved at runtime. This usually involves introspecting on the receiver's virtual function table. Thus if you try calling a virtual member function on a null pointer, even if te function doesn't access this, it will still cause a crash. Try this out if you're curious!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
5

The results of calling a member function using a null pointer to an object is undefined behavour in c++ so it can do anything.

In this case it's likely because it's rewritten your function as it it was like this

void mprint(A* this);

and your call like this

mprint(0);

So it's just called it as if it was an ordinary function and passed the null pointer as a parameter which you never then actually use in any way. That explains why it doesn't crash, but the compiler is free to do pretty much anything

jcoder
  • 29,554
  • 19
  • 87
  • 130
  • I like this answer, because many people imagine that the functions are included in the instance (and not in the class). And therefor they're worried about excessive memory use... I'd say member functions are just syntaxic sugar for your example. – Tristram Gräbener Mar 25 '11 at 10:57
2

Simple Answer: Because mprint() is not using any of the member variables of the class

Detailed Answer: When a method of a class is called, class instance is passed on to the callee function (normally as the first argument, however, in some calling conventions such as __thiscall, this is passed in a register). This class instance is used to access all the member variables that are used in the callee method.

In this case, this instance is NULL but this doesnt make any difference since no member variables are being used in the callee method. Try changing your code such that you print the value of a member variable in mprint() method and you will get the crash.

Aamir
  • 14,882
  • 6
  • 45
  • 69
  • I am not a C++ guy, but you are calling the method on a NULL reference and the method is not static. Is this how C++ works? It allows you to call non-static member method on NULL reference? Just curious, thanks! – Jan Zyka Mar 25 '11 at 10:53
  • it's undefined behavour I believe that will likely "work" as you expect in most compilers due to the obvious way to implement it. – jcoder Mar 25 '11 at 10:54
  • @JanZyka: As Erik's answer says: it is undefined behavior. Which in this particular case happens to "work normally". – Jon Mar 25 '11 at 10:54
  • @Jan: Such a method which doesnt use any member variable is a good candidate to be a static method. Remember that data members always have separate copies for every instance of the class but functions have only one copy that is general purpose for every instance. And can be called directly even. C++, unlike some other languages doesn't impose compile time restrictions on calling method in this way. – Aamir Mar 25 '11 at 10:56
  • 1
    @Jan Zyka: One more small thing..its a not a NULL *reference*, its a NULL *pointer* – Naveen Mar 25 '11 at 10:56
  • It's not my question, don't blame me not making it static :D Ok, understand. It's always nie to learn something new. – Jan Zyka Mar 25 '11 at 11:02
1

Being able of invoking non-virtual member functions on non-valid pointers even enables encoding the information associated to an object in the pointer itself. For example:

#include <iostream>

class MagicInteger {

  public:

   static MagicInteger* fromInt (int x) {
     return reinterpret_cast<MagicInteger*>(x);
   }

   int getValue() {
     return static_cast<int>(reinterpret_cast<intptr_t>(this));
   }       

  private:
   // forbid messing around
   MagicInteger ();  
   MagicInteger (MagicInteger&);
   MagicInteger& operator=(const MagicInteger&);
};

int main (void) {
  MagicInteger* i = MagicInteger::fromInt(6);
  std::cout << "Value is " << i->getValue() << std::endl;
  return 0;
}

This can also be used to implement tagged pointers, that is, pointers that contain meta-information about the pointee.

These two idioms are used in Google Chrome's javascript VM V8 to represent 31-bit integers

noe
  • 1,684
  • 1
  • 17
  • 35
  • Don't do this. If you need to be able to store either a pointer or a value, use a `union`. – Ben Voigt Mar 26 '11 at 20:27
  • @Ben agreed that this is not a good coding practice. In V8, however, they have a class hierarchy in which class `Object` is the root. They maintain a list of `Object*` that are actually tagged. Class `Smi`which implements the trick I mentioned, inherits from `Object`, so they are not able to use a union directly. – noe Mar 26 '11 at 20:53
  • @ncasas: Something else must be going on. This wouldn't work alongside polymorphism, virtual calls would just fail. So the code iterating the list of `Object*` must be aware of the trick. But then you can have a list of `union { Object*; int; }` instead. – Ben Voigt Mar 26 '11 at 21:07
  • @Ben you're completely right, there's no polymorphism and the code is aware of the trick. However, they aim at packing together the union of the `Object*` (30 bits due to forced alignment) and the integer (31 bit), plus the discriminator for distinguishing the actual contents (1 bit). If they used a union, they wouldn't be able to fit everything in 32 bits. – noe Mar 26 '11 at 23:48
  • @ncasas: How would a `union` prevent things from fitting? The whole point of a union is that the different members share storage. – Ben Voigt Mar 26 '11 at 23:52
  • @ncasas: Please note that it's not storing an integer inside a pointer that I strongly object to (although I would ask whether the space savings is worth the increased complexit, for V8 it probably is). But there's no good reason to store the integer handling code inside the type pointed to and then call instance methods with an invalid pointer. Instead, a smart pointer type can do both, and not suffer undefined behavior. e.g. `struct ptr_or_int { Object* m_ptr; Object* operator->() { return m_ptr; } int as_int() { return reinterpret_cast(m_ptr); } };` – Ben Voigt Mar 27 '11 at 00:02
0

This is completely legal call.

lets understand how it works

when a new object is creates its member variables are created.

What about member functions? Member function are not allocated news there is always one copy of all member function. By default a member variable is added to every member function that is this pointer which is pointing to the object itself.
When there is no object present that is object pointer is null value. It doenst matter because you are not accesssing it any way. You will get in problems if you use this pointer of any of the member variable in the method. This is because member variable are not valid in case of null pointer.

in MFC we have GetSafeHwnd() method for CWnd. This works on same principle.

Vijay
  • 2,021
  • 4
  • 24
  • 33
  • 1
    No, it's not "legal". It's *undefined behavior*. MFC gets away with it only because MFC is locked to a particular compiler which makes stronger guarantees than the C++ language itself. – Ben Voigt Mar 26 '11 at 23:56