matovitch
matovitch

Reputation: 1284

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;
}

Upvotes: 2

Views: 849

Answers (2)

jsantander
jsantander

Reputation: 5102

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]

Upvotes: 2

lifeOfPI
lifeOfPI

Reputation: 318

My first guess would be that (dynamic_cast<B&>(aArray[1])).x = 2; will/should throw an exception because aArray[1]cannot be downcasted....

Lets say I will work without exception or you would use static_cast<> instead, you would definitely corrupt your memory!

imagine: sizeof(A) = 4 bytes; sizeof(B) = 8 bytes

memory after

A* aArray = new B[2];

[-----B------|------B-----] memory

[-----0------|------1-----] indices

you are saving it to A*. So whats happening if you i.e do

(static_cast<B&>(aArray[1])).x = 2; 

[[--A--]B[--A--]]|----B-----]

[-------x--------|--- you start writing memory on this position.

Upvotes: 3

Related Questions