0

I'm looking for clarification on this bit of code. The call to A::hello() works (I expected a segv). The segfault does come through on the access to member x, so it seems like the method resolution alone doesn't actually dereference bla?

I compiled with optimization off, gcc 4.6.3. Why doesn't bla->hello() blow up? Just wonderin' what's going on. Thanks.

class A
{

public:
    int x;

    A() { cout << "constructing a" << endl; }

    void hello()
    {
        cout << "hello a"  << endl;
    }

};

int main()
{
    A * bla;
    bla = NULL;
    bla->hello();    // prints "hello a"
    bla->x = 5;      // segfault
}
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
jarvisteve
  • 926
  • 8
  • 14

5 Answers5

5

Your program exhibits undefined behavior. "Seems to work" is one possible manifestation of undefined behavior.

In particular, bla->hello() call appears to work because hello() doesn't actually use this in any way, so it just happens not to notice that this is not a valid pointer.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
3

You are dereferencing NULL pointer, i.e. trying to access object stored at address NULL:

bla = NULL;
bla->hello();
bla->x = 5;

which results in undefined behavior, which means that anything can happen, including the seg fault while assigning 5 to the member x and also including deceptive "works as expected" effect while invoking the hello method.

LihO
  • 41,190
  • 11
  • 99
  • 167
2

As long as a member function doesn't dereference this, it's usually "safe" to call it, but you're then in undefined behavior land.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
2

In theory, this is undefined behavior. In practice, you do not use this pointer when calling hello(), it does not reference your class at all, and therefore works and does not generate memory access violation. When you do bla->x, however, you are trying to reference a memory though bla pointer which is uninitialized, and it crashes. Again, even in this case there is no guarantee that it will crash, this is undefined behavior.

  • Yep, since hello is non-virtual, no access to the ref pointer is needed to invoke it. The type of the pointer is used by the compiler to resolve the specific hello method, but that's it. (There's even a vague possibility that this behavior is "defined".) – Hot Licks Oct 16 '13 at 17:06
  • @HotLicks: Well, I would not say it is defined because C++ standard does not tell how exactly the language should be implemented. So from language standard point of view it is probably not. But from some ABI point of view it could as well be well defined :) –  Oct 16 '13 at 17:21
2

At least in the typical implementation, when you call a non-virtual member function via a pointer, that pointer is not dereferenced to find the function.

The type of the pointer is used to determine the scope (context) in which to search for the name of the function, but that happens entirely at compile time. What the pointer points at (including "nothing", in the case of a null pointer) is irrelevant to finding the function itself.

After the compiler finds the correct function, it typically translates a call like a->b(c); into something roughly equivalent to: b(a, c); -- a then becomes the value of this inside the function. When the function refers to data in the object, this is dereferenced to find the correct object, then an offset is normally applied to find the correct item in that object.

If the member function never attempts to use any of the object's members, the fact that this is a null pointer doesn't affect anything.

If, on the other hand, the member function does attempt to use a member of the object, it'll attempt to dereference this to do that, and if this is a NULL pointer, that won't work. Likewise, calling a virtual function always uses the vtable pointer, which is in the object, so attempting to call a virtual function via a null pointer can be expected to fail (regardless of whether the code in the virtual function refers to data in the object or not).

As I said to start with, I'm talking about the typical implementation here. From the viewpoint of the standard, you simply have undefined behavior, and that's the end of it. In theory, an implementation doesn't have to work the way I've described. In reality, however, essentially all the reasonably popular implementations of C++ (e.g., MS VC++, g++, Clang, and Intel) all work very similarly in these respects.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111