57

I've seen code like this in a couple of old projects:

class Class {
    static void Method() {}
};

((Class*)0)->Method();

This code contains undefined behavior because it includes dereferencing a null pointer (no matter what happens afterwards). It really makes no sense - the cast is there to feed the type name to the compiler and whoever wrote the code above could have written this instead:

Class::Method();

and the latter would be okay.

Why would anyone write the former code? Is it a known idiom from some good old days or what?

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • And here I thought it was just Java that allowed calling class members via instance references. Huh. – T.J. Crowder Sep 08 '14 at 07:28
  • There is another interesting similar thing. Even if the method is not static, if you don't use `this` or use any fields from class it wont produce IllegalAccess error. like `class C {void f() { cout << "something"; }} C* instance = nullptr; instance->f();` – Mohammad Jafar Mashhadi Sep 08 '14 at 07:30
  • What makes you think a null pointer will be _dereferenced_ by this code? Why would x->staticMethod() dereference x? – gnasher729 Sep 08 '14 at 07:31
  • 2
    I suspect this kind of thing comes from macros that can be used with regular or static methods. – Barmar Sep 08 '14 at 07:32
  • 1
    I am not certain the call in itself is strictly doing any "dereferencing". Though that might happen in the function itself. – Galik Sep 08 '14 at 07:32
  • 2
    It's kind of like the fake dereferencing in the `offsetof()` macro that people wrote before it was added to the language. – Barmar Sep 08 '14 at 07:33
  • 17
    It's obviously a bad thing to do - my best guess is that originally `Method` was not static (but didn't access any instance data) and someone used this hack to invoke it. Later someone else realised that `Method` needed to be static and changed it, but didn't fix all the places that it was being called from. – Paul R Sep 08 '14 at 07:33
  • It's probably a very bad attempt to mimicate `abort()`. – 101010 Sep 08 '14 at 07:33
  • @gnasher729: The Standard says `a->b` is equivalent to `(*a).b` and `*a` is dereferencing. – sharptooth Sep 08 '14 at 07:39
  • @40two: This will likely never call abort. Most likely it just works. I cannot imagine how the C++ runtime would call `abort()` from there. – sharptooth Sep 08 '14 at 07:40
  • 2
    Its far more common to see a null pointer *variable* used with this. Ex: VC++ DevCon 1999, DonBox touted, `CComObject* pObj = NULL; HRESULT hr = pObj->CreateInstance(&pObj);` "You know you can do that, right?" Well, [**read this**](http://stackoverflow.com/a/3498473/1322972) and decide which side of the fence you reside on. Its clear where Don hangs out. – WhozCraig Sep 08 '14 at 07:46
  • @sharptooth it won't call `abort()` it will break the program intentionally. – 101010 Sep 08 '14 at 07:49
  • I met a developer (now manager) with 20 years' commercial C++ experience who wrote this, then was entirely baffled as to why the rest of us told him, during a very fortunate code review, that it was wrong. Seemingly hadn't even heard of `::` notation, and became very confused. -.- – Lightness Races in Orbit Sep 09 '14 at 00:43

1 Answers1

67

Static member functions were added into C++ in 1989, in Release 2.0 of the AT&T C++ Language System (pre-standardisation). Prior to that, the static keyword could not be used to declare static member functions, so code authors used workarounds, principally the one you have observed of indirecting a null pointer.

In the Selected Readings accompanying version 2.0 of the AT&T C++ Language System, in section 1-22, Stroustrup writes:

It was also observed that nonportable code, such as:

((X*)0)->f();

was used to simulate static member functions. This trick is a time bomb because sooner or later someone will make an f() that is used this way virtual and the call will fail horribly because there is no X object at address zero. Even where f() is not virtual such calls will fail under some implementations of dynamic linking.

Your code was written to compile under Cfront 1.0 or by someone who was not aware at the time of the addition of static member functions to the language.

The annotation of the member function with static is indeed a puzzle, as Cheers and hth. - Alf has observed; Cfront 1.0 would have rejected that code with:

error:  member Method() cannot be static

so it cannot have been there initially. I think Potatoswatter is most likely correct; static was added at a later date to document and enforce the static method attribute of Method, once a C++ 2.0 compiler could be guaranteed to be available, but without the calling code being updated. To confirm this you'd need to interview the original programmer(s) or at least examine source control history (if any exists).

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Why doesn't Stroustrup mention the more egregious problem of undefined behavior? He argues with implementation details. – usr Sep 08 '14 at 12:41
  • 1
    @usr It goes without saying that the crash *in practice* is due to undefined behavior *in theory*. – Potatoswatter Sep 08 '14 at 12:44
  • @Potatoswatter No matter what f does, an optimizing compiler can treat the call as unreachable. This is frequently being done. A crash is not at all the only practical consequence. Deletion of code is another. That's why arguing with implementation details is inferior to just pointing out UB. The failure modes follow from the presence of UB. – usr Sep 08 '14 at 12:46
  • 1
    @usr: Stroustrup couldn't argue about undefined behavior pre 1998, such as for code before 1989 (this code), because that would be meaningless. The language was only informally and partially defined then, by his book "The C++ Programming Language". – Cheers and hth. - Alf Sep 08 '14 at 12:50
  • 3
    **-1** Generally nice answer! But, twice incorrect conclusion "Your code was written to compile under C++ 1.x or by someone who was not aware at the time of the addition of static member functions to the language." Since the code uses the `static` keyword neither of these possibilities can be true. The code would not compile in a version of C++ without `static` member functions. And the programmer using `static` could not be unaware of it. Sorry I didn't read that far earlier. – Cheers and hth. - Alf Sep 08 '14 at 14:08
  • 4
    @Cheersandhth.-Alf Likely the client (function call) code is cruftier, and the library (class definition) was updated with `static` after it was introduced. Or, it's the product of two programmers, one of whom was anachronistic. – Potatoswatter Sep 08 '14 at 15:36