Search code examples
c++rttidynamic-cast

dynamic_cast segfault with RTTI enabled


I was curious about about how dynamic_cast can cast an element of an array to a larger class (does it shift all the other elements ?). So I wrote a small code to try it. But I was suprised as it compiles but segfault at the first line. Why ?

#include <iostream>

class A
{
public:
    virtual ~A() {}
};

class B : public A
{
public:
    int x;
};

class C : public A
{
public:
    int x;
    int y;
};

int main()
{
    A* aArray = new B[2];
    (dynamic_cast<B&>(aArray[0])).x = 1; //segfault here
    (dynamic_cast<B&>(aArray[1])).x = 2;

    (dynamic_cast<C&>(aArray[0])).y = 3;

    std::cout << (dynamic_cast<B&>(aArray[1])).x << std::endl;

    return 0;
}

Solution

  • Here I go. I compiled and run with gdb

    First I set the print object option:

    (gdb) set print object
    

    check the address of aArray

    (gdb) print aArray
    $1 = (B *) 0x8003a404
    

    check the size of A and B

    (gdb) print sizeof(B)
    $2 = 8
    (gdb) print sizeof(A)
    $3 = 4
    

    get the address of aArray[0]

    (gdb) print &aArray[0]
    $4 = (B *) 0x8003a404
    

    get the address of aArray[1]

    (gdb) print &aArray[1]
    $5 = (A *) 0x8003a408
    

    Quoting the answer in linked question:

    if you look at the expression p[1], p is a Base* (Base is a completely-defined type) and 1 is an int, so according to ISO/IEC 14882:2003 5.2.1 [expr.sub] this expression is valid and identical to *((p)+(1))

    and

    From 5.7 [expr.add] / 5, when an integer is added to a pointer, the result is only well defined when the pointer points to an element of an array object and the result of the pointer arithmetic also points the an element of that array object or one past the end of the array. p, however, does not point to an element of an array object, it points at the base class sub-object of a Derived object. It is the Derived object that is an array member, not the Base sub-object.

    In this particular case, the implementation of the pointer arithmetic is to increase the pointer memory by the size of the pointer type (but refer to this answer for additional nuances).

    The effect of aArray + 1 is to point at the 2nd element of an array of objects of type A

    That matches:

    (gdb) print (A*)(((char *)aArray) + sizeof(A))
    $6 = (A *) 0x8003a408
    

    ... however aArray is really an array of objects of type B.

    So the second element of the array is:

    (gdb) print &((B *)aArray)[1]
    $6 = (B *) 0x8003a40c
    

    so you ended up pointing somewhere in the middle of the first B object... and that access caused the segmentation fault.

    For an alternative explanation an example refer to: [http://www.parashift.com/c++-faq/array-derived-vs-base.html]