Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does upcasting a null pointer lead to undefined behavior

I'm wondering whether the following code leads to undefined behavior:

#include <cstddef>
#include <cstdio>

struct IA { 
  virtual ~IA() {}
  int a = 0;
};
struct IB {
  virtual ~IB() {}
  int b = 0;
};
struct C: IA, IB {};

int main() {
  C* pc = nullptr;
  IB* pib = pc;
  std::printf("%p %p", (void*)pc, (void*)pib);
}
like image 963
Lingxi Avatar asked Apr 24 '15 14:04

Lingxi


2 Answers

Upcasting a null pointer is well-defined to give you another null pointer:

4.10p3:

A prvalue of type "pointer to cv D", where D is a class type, can be converted to a prvalue of type "pointer to cv B", where B is a base class of D. ... The null pointer value is converted to the null pointer value of the destination type.

like image 95
aschepler Avatar answered Oct 02 '22 07:10

aschepler


Stroustrup discusses this case in section 4.5 of his 1989 multiple inheritance paper [PDF]:

The solution is to elaborate the conversion (casting) operation to test for the pointer-value 0 [...]

The added complexity and run-time overhead are a test and an increment.

The implementation checks explicitly for null-values and ensures that the result of the cast is still a null-value. This was true in C++98 and has not changed with C++11 and nullptr.

This is especially important in the case of multiple base classes, where a cast from a derived class to one of the base classes might require changing the actual value of the pointer.

In your example, the layout of C in memory will first contain the bytes for IA, followed by the bytes for IB. Casting to IA is trival, as a pointer to the beginning of C will also point to the beginning of the IA part of C. Casting to IB on the other hand, requires shifting the C pointer by the size of IA. Performing this shifting in the nullptr case would lead to a non-null pointer after the cast, hence the special treatment for nulls.

As pointed out by aschepler, the relevant section in the standard is [conv.ptr] §4.10:

A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class [...] of D. [...] The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

like image 21
ComicSansMS Avatar answered Oct 02 '22 05:10

ComicSansMS