Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why sizeof(Base) is not different of sizeof(Derived)

I think sizeof(Base) should be 12. Why is it 16?

Without the virtual function, I get 4 and 8.

class Base{
  public:
    int i;
    virtual void Print(){cout<<"Base Print";}
};

class Derived:public Base{
  public:
    int n;
    virtual void Print(){cout<<"Derived Print";}
};

int main(){
  Derived d;
  cout<<sizeof(Base)<<","<<sizeof(d);
  return 0;
}

expected result:12,16

actual result:16,16

like image 583
Z.Hc Avatar asked Jan 02 '23 03:01

Z.Hc


1 Answers

why sizeof(Base) is not different of sizeof(Derived)

Because of the alignment introduced by the compiler.

That is architecture-dependent, but for sake of simplicity, I am going to assume we are referring a 64-bit architecture.

Scenario 64 bit / Clang 8.0.

The alignment of the type Base is 8 bytes:

alignOfBase(): # @alignOfBase()
  mov eax, 8
  ret

The layout of Base is composed by the variable member (int) and the virtual table (vtptr).

If we assume a "common" architecture where:

  • int is 4 bytes size.
  • vtptr is a pointer. On a 64 bit architecture is 8 bytes size.

We should have a sum of 4 + 8 = 12, as you expect.

However, we need to remember the alignment of Base is 8 bytes. Therefore consecutive Base types should be stored in location multiple of 8.

In order to guarantee that, the compiler introduces padding for Base. That's why Base is 16 bytes size.

For example, if we consider 2 consecutive Base (base0 and base1) without padding:

0:  vtptr (base 0) + 8
8:  int   (base 0) + 4
12: vtptr (base 1) + 8  <--- Wrong! The address 12 is not multiple of 8.
20: int   (base 1) + 4

With padding:

0:  vtptr (base 0) + 8
8:  int   (base 0) + 4+4   (4 padding)
16: vtptr (base 1) +8      <--- Fine! The adress 16 is multiple of 8.
24: int   (base 1) +4+4    (4 padding)

The same story is for Derived type.

The layout of Derived should be: vtptr + int + int, that is, 8 + 4 + 4 = 16.

The alignment of Derived is 8 too:

alignOfDerived(): # @alignOfDerived()
  mov eax, 8
  ret

Indeed, in this case, there is no need to introduce padding in order to keep the Derived aligned with the memory. The layout size will be the same as the real size.

0:   vtptr (Derived 0)
8:   int   (Derived 0)
12:  int   (Derived 0)
16:  vtptr (Derived 1)  <---- Fine. 16 is multiple of 8.
24:  int   (Derived 1)
28:  int   (Derived 1)
like image 86
BiagioF Avatar answered Jan 12 '23 11:01

BiagioF