Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a practical benefit to casting a NULL pointer to an object and calling one of its member functions?

Ok, so I know that technically this is undefined behavior, but nonetheless, I've seen this more than once in production code. And please correct me if I'm wrong, but I've also heard that some people use this "feature" as a somewhat legitimate substitute for a lacking aspect of the current C++ standard, namely, the inability to obtain the address (well, offset really) of a member function. For example, this is out of a popular implementation of a PCRE (Perl-compatible Regular Expression) library:

#ifndef offsetof
#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
#endif

One can debate whether the exploitation of such a language subtlety in a case like this is valid or not, or even necessary, but I've also seen it used like this:

struct Result
{
   void stat()
   {
      if(this)
         // do something...
      else
         // do something else...
   }
};

// ...somewhere else in the code...

((Result*)0)->stat();

This works just fine! It avoids a null pointer dereference by testing for the existence of this, and it does not try to access class members in the else block. So long as these guards are in place, it's legitimate code, right? So the question remains: Is there a practical use case, where one would benefit from using such a construct? I'm especially concerned about the second case, since the first case is more of a workaround for a language limitation. Or is it?

PS. Sorry about the C-style casts, unfortunately people still prefer to type less if they can.

like image 338
Zoli Avatar asked Apr 02 '10 22:04

Zoli


2 Answers

The first case is not calling anything. It's taking the address. That's a defined, permitted, operation. It yields the offset in bytes from the start of the object to the specified field. This is a very, very, common practice, since offsets like this are very commonly needed. Not all objects can be created on the stack, after all.

The second case is reasonably silly. The sensible thing would be to declare that method static.

like image 94
bmargulies Avatar answered Nov 12 '22 14:11

bmargulies


I don't see any benefit of ((Result*)0)->stat(); - it is an ugly hack which will likely break sooner than later. The proper C++ approach would be using a static method Result::stat() .

offsetof() on the other hand is legal, as the offsetof() macro never actually calls a method or accesses a member, but only performs address calculations.

like image 31
Lars Avatar answered Nov 12 '22 12:11

Lars