Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

References to the same base classes must have separate offsets in memory

I've discovered some inconsistencies between compilers with this program,

struct A {
};

struct B : public A {
    float m;
};

struct C : public A {
    B b;
    float n;
};

struct D : public A {
    float n;
    B b;
};

static_assert(sizeof(A) == 1, "");
static_assert(sizeof(B) == 4, "");
static_assert(sizeof(C) == 8, ""); // most compilers say this is 12
static_assert(sizeof(D) == 8, "");

Most compilers assert on sizeof(C) == 8 saying that the sizeof(C) is actually 12. The only compiler I've found that doesn't and says it's 8 is Microsoft Visual Studio 2010.

The reason I've been told, by someone smarter then me, is that there are two separate references of A within B that need to retain individual offsets different from one another. First, the A derived from C is at offset 0 and the second A inside member b can not be at the same offset as the first A at 0 so 4 bytes of padding is inserted.

As most compiler's have implemented this behavior I was wondering what case do you need to make sure both A's have different references? Looking for some intuition on why this is the case?

Someone said it may be a condition required by the standard and we were curious on what is the reason for it?

Thank you

like image 386
coderdave Avatar asked Dec 05 '12 23:12

coderdave


3 Answers

The standard definitely mandates that the address of each object of the same type is different. The relevant clause is 5.10 [expr.eq] paragraph 1:

Two pointers of the same type compare equal if and only if they are both null, both point to the same function, or both represent the same address (3.9.2).

This is needed to, well, distinguish the two objects. Objects have both a value and an identity. For a base class subobject it is reasonable to have the same address as the containing class. For a member of a class, you can distinguish the identity of the two objects by their type, i.e., it is OK for them to have the same address. For two object of the same type you'd still need something to distinguish the objects for their identity.

like image 86
Dietmar Kühl Avatar answered Nov 03 '22 13:11

Dietmar Kühl


Yes, this is mentioned in 10p8:

A base class subobject may be of zero size (Clause 9); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address (5.10).

In C you have two As, one is inherited and the other is part of B. Microsoft aggressively and wrongly employs the empty base class optimization here when it shouldn't. I believe it is known bug, but it really is hard to find the bug report on Microsoft Connect.

like image 34
Jesse Good Avatar answered Nov 03 '22 11:11

Jesse Good


In C++, an object is uniquely determined by a pair of data: its address and its type.

As you rightly observed, both C and D contain two distinct subobjects A for which this must be true. However, depending on how you lay out B in memory, you can see how it might not be possible to put both A-subobjects at different addresses within an 8-byte structure.

Why don't you print out the actual numerical addresses of the subobjects?

like image 1
Kerrek SB Avatar answered Nov 03 '22 12:11

Kerrek SB