How is the size of a C++ class determined?

Gabriel Southern picture Gabriel Southern · Jan 24, 2013 · Viewed 13.7k times · Source

Summary: How does the compiler statically determine the size of a C++ class during compilation?

Details:

I'm trying to understand what the rules are for determining how much memory a class will use, and also how the memory will be aligned.

For example the following code declares 4 classes. The first 2 are each 16 bytes. But the 3 is 48 bytes, even though it contains the same data members as the first 2. While the fourth class has the same data members as the third, just in a different order, but it is 32 bytes.

#include <xmmintrin.h>
#include <stdio.h>

class TestClass1 {
  __m128i vect;
};

class TestClass2 {
  char buf[8];
  char buf2[8];
};

class TestClass3 {
  char buf[8];
  __m128i vect;
  char buf2[8];
};

class TestClass4 {
  char buf[8];
  char buf2[8];
  __m128i vect;
};


TestClass1 *ptr1;
TestClass2 *ptr2;
TestClass3 *ptr3;
TestClass4 *ptr4;
int main() {
  ptr1 = new TestClass1();
  ptr2 = new TestClass2();
  ptr3 = new TestClass3();
  ptr4 = new TestClass4();
  printf("sizeof TestClass1 is: %lu\t TestClass2 is: %lu\t TestClass3 is: %lu\t TestClass4 is: %lu\n", sizeof(*ptr1), sizeof(*ptr2), sizeof(*ptr3), sizeof(*ptr4));
  return 0;
}

I know that the answer has something to do with alignment of the data members of the class. But I am trying to understand exactly what these rules are and how they get applied during the compilation steps because I have a class that has a __m128i data member, but the data member is not 16-byte aligned and this results in a segfault when the compiler generates code using movaps to access the data.

Answer

Eric Postpischil picture Eric Postpischil · Jan 24, 2013

For POD (plain old data), the rules are typically:

  • Each member in the structure has some size s and some alignment requirement a.
  • The compiler starts with a size S set to zero and an alignment requirement A set to one (byte).
  • The compiler processes each member in the structure in order:
    1. Consider the member’s alignment requirement a. If S is not currently a multiple of a, then add just enough bytes S so that it is a multiple of a. This determines where the member will go; it will go at offset S from the beginning of the structure (for the current value of S).
    2. Set A to the least common multiple of A and a.
    3. Add s to S, to set aside space for the member.
  • When the above process is done for each member, consider the structure’s alignment requirement A. If S is not currently a multiple of A, then add just enough to S so that it is a multiple of A.

The size of the structure is the value of S when the above is done.

Additionally:

  • If any member is an array, its size is the number of elements multiplied by the size of each element, and its alignment requirement is the alignment requirement of an element.
  • If any member is a structure, its size and alignment requirement are calculated as above.
  • If any member is a union:
    1. Set S to the size of the largest member.
    2. Set A to the least common multiple of the alignments of all the members.
    3. If S is not a multiple of A, add just enough to S to make it a multiple of A.

Consider your TestClass3:

  • S starts at 0 and A starts at 1.
  • char buf[8] requires 8 bytes and alignment 1, so S is increased by 8 to 8, and A remains 1.
  • __m128i vect requires 16 bytes and alignment 16. First, S must be increased to 16 to give the right alignment. Then A must be increased to 16. Then S must be increased by 16 to make space for vect, so S is now 32.
  • char buf2[8] requires 8 bytes and alignment 1, so S is increased by 8 to 24, and A remains 16.
  • At the end, S is 24, which is not a multiple of A (16), so S must be increased by 8 to 32.

So the size of TestClass3 is 32 bytes.

For elementary types (int, double, et cetera), the alignment requirements are implementation dependent and are largely determined by the hardware. On many processors, it is faster to load and store data when it has a certain alignment (usually when its address in memory is a multiple of its size). Beyond this, the rules above follow largely from logic; they put each member where it must be to satisfy alignment requirements without using more space than necessary.