2

I cannot understand the reason for difference in size of C and C2 in the following code:

#include <iostream>

struct A {
    int* x;
};

struct B {
    A a;
    int y;
};

struct C : B {
   int z;
};

struct B2 : A {
   int y;
};

struct C2 : B2 {
   int z;
};
    
int main()
{
    std::cout << sizeof(A) << std::endl; // 8
    std::cout << sizeof(B) << std::endl; // 16
    std::cout << sizeof(C) << std::endl; // 24
    std::cout << sizeof(B2) << std::endl; // 16
    std::cout << sizeof(C2) << std::endl; // 16
}

https://wandbox.org/permlink/GEWj2LQxloC34lNS

What I (probably) understand is that,

  • C has the following memory layout
|0      |4      |8      |12     |16      |20     |
|A::x-----------|B::y---|padding|C::z----|padding|
  • C2 has the following memory layout.
|0      |4      |8      |12      |
|A::x-----------|B::y---|C::z----|

In C, it seems that the padding of structure B remains, but in C2, it seems that the padding of structure B2 is packed. What is the cause of this difference? (Is it defined in the C++ standard? What kind of rule is it?)

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
gana
  • 43
  • 4
  • 1
    Where which amout of padding is added is implementation-defined. – flowit Mar 11 '21 at 17:16
  • "What is the cause of this difference?" what do you mean exactly? The different size of `C` and `C2` ? Are you asking why `sizeof(C)=24` while `sizeof(C2) == 16` ? – 463035818_is_not_an_ai Mar 11 '21 at 17:16
  • @Hammer User.... there is no multiple inheritance in this question, that was the wrong duplicate. – Ben Voigt Mar 11 '21 at 17:19
  • @largest_prime_is_463035818 What I want to know is why the padding of B remains in C, but the padding of B2 does not remain in C2. – gana Mar 11 '21 at 17:27
  • 2
    [this question and accepted answer](https://stackoverflow.com/questions/53837373/standard-layout-and-tail-padding) goes into some more detail (might be a considered a duplicate) – Kevin Mar 11 '21 at 17:43
  • @Kevin yes that's the right duplicate. – Ben Voigt Mar 11 '21 at 17:55

1 Answers1

5

C++ allows subobjects introduced in a derived class to overlap (padding in) base subobjects as long as those objects are not standard-layout. It does not allow any overlap between member subobjects, or overlap of a standard-layout base subobject.

Look at the size and layout of struct B to understand why struct C has internal padding.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I don't think this really answers the question. Why can't `C::z` overlap the padding in the base object `B`, but `C2::z` *can* overlap the padding in the base object `B2`? Or am I not fully understanding what "It does not allow any overlap between member subobjects" means? – Kevin Mar 11 '21 at 17:31
  • @Kevin: You're right, I answered for `struct C { B base; int z; };` Now expanded. – Ben Voigt Mar 11 '21 at 17:33
  • 1
    I think I understand now. `struct B` is standard-layout, but `struct B2` is not - one of the requirements for standard layout according to [cppreference](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType) is "Has all non-static data members and bit-fields declared in the same class (either all in the derived or all in some base)" – Kevin Mar 11 '21 at 17:40
  • 1
    Yes, that's the essential difference. Standard-layout subobjects have to be compatible with external code... if for example you pass a `struct C` to a function accepting `B*` with output parameter semantics and that function used `memcpy` to write to it, it'll trash the internal padding, which would ruin your day if some other object overlapped the internal padding. C++ makes that safe when `B` is standard-layout. – Ben Voigt Mar 11 '21 at 17:54