sizeof: When The Whole is Greater Than The Sum Of Its Parts

Running  the program:

	#include <iostream>

	struct A
	{
		char c1;
		int i1;
		char c2;
		int i2;
	};

	int main()
	{
		std::cout << "the size of A::c1: " << sizeof(A::c1) << std::endl;
		std::cout << "the size of A::i1: " << sizeof(A::i1) << std::endl;
		std::cout << "the size of A::c2: " << sizeof(A::c2) << std::endl;
		std::cout << "the size of A::i2: " << sizeof(A::i2) << std::endl;
		std::cout << "                   --" << std::endl;
		std::cout << "Total:             " << sizeof(A::c1) + 
			sizeof(A::i1) + sizeof(A::c2) +
			sizeof(A::i2) <<  std::endl;

		std::cout << "\nThe size of A: " << sizeof(A) << std::endl;

		return 0;
	}

Generates this output:

the size of A::c1: 1
the size of A::i1: 4
the size of A::c2: 1
the size of A::i2: 4
                   --
Total:             10

The size of A: 16

(Compiled by both Linux/GCC 4.9.2  and Visual Studio 2015, using x86 CPU)

The structure A occupies 6  bytes more than the sum of its members.

the mystery thickens when we check the output for structure B having the same elements in a slightly different order:

	#include <iostream>

	struct B
	{
		char c1;
		char c2;
		int i1;
		int i2;
	};

	int main()
	{
		std::cout << "the size of B::c1: " << sizeof(B::c1) << std::endl;
		std::cout << "the size of B::i1: " << sizeof(B::i1) << std::endl;
		std::cout << "the size of B::c2: " << sizeof(B::c2) << std::endl;
		std::cout << "the size of B::i2: " << sizeof(B::i2) << std::endl;
		std::cout << "                   --" << std::endl;
		std::cout << "Total:             " << sizeof(B::c1) + 
			sizeof(B::i1) + sizeof(B::c2) +
			sizeof(B::i2) <<  std::endl;


		std::cout << "\nThe size of B: " << sizeof(B) << std::endl;

		return 0;
	}

Now the output is:

the size of B::c1: 1
the size of B::i1: 4
the size of B::c2: 1
the size of B::i2: 4
                   --
Total:             10

The size of B: 12

Just  moving  c1   deceleration caused the B structure to shrink by 4 bytes!  Yet it’s total size is  still  larger than the sum of it’s members.

why does it happen?   The hardware is  optimized to read data from memory  address which are multiples of the data size  : 4 bytes  integers (int32) are read from addresses which are multiples of 4.  Assuming the first address is 0  valid address for int32  would be 0, 4, 8, 12 .. 4n.   1 byte sized chars are read from addresses  (0,1,2,3.. n) .

In an additional constraint ,   the C++ 11  standard requires that data members are allocated in memory in the same order they are declared,  assuming they have the  same access access-specifier (private, public  protected).

It just happens that in  our  A,B structures, all the data members have public access specifier, causing the compiler to layout the member in the same  order I defined them.

For simplicity lets assume memory start with address 0:   When laying out an object instantiated from struct A,  The compiler will start with address 0  allocating that byte for  char member c1,   It then must skip addresses 1,2,3  as they are not multiples of 4  and not suitable to store  an int32 data type. So i1 get  allocated addresses 4-7.   Next,   char member c2  is assigned address 8.   The following 3 addresses are again  skipped,  and   i2 is  assigned address  12-15.
With 10 spaces occupied by actual members and 6 bytes “skipped” (the proper term is,  padding) we get the 16 bytes as the size of  structure A.

image

with Structure B we  need only two padding bytes (after allocating space for c2) so it’s final size  is 12 instead of 16:

image

Note: The developer can instruct the compiler  to store the data members with no regard for alignment,  however, it will cost heavily  in performance since the CPU will need to shuffle the bytes around after reading them.

Empty classes and classes including virtual methods, are another examples where an object is larger than it appears to be at first glance:

	#include <iostream>

	struct A
	{
		void Hi()
		{
			std::cout << "Hi" << std::endl;
		}
	};
	
	struct B
	{
		virtual void Hi()
		{
			std::cout << "Hi" << std::endl;
		}
	};

	int main()
	{
		std::cout << "the size of A: " << sizeof(A) << std::endl;
		std::cout << "the size of B: " << sizeof(B) << std::endl;
	}

Here the output is:

the size of A: 1
the size of B: 4

The size of structure A is 1  for no apparent  reason,  it has no data members,  So where did this byte come from?
Let’s suppose  we could have a class (let’s call it O)  who’s size is zero, that is sizeof(O)=0 , In that case, I could have written  this declaration :

  	struct C
   	  {
	  O o1;
	  O o2;
	  O o3;
   	 };

the compiler will have to place o1 at the first available address  – 0  and place o2 and o3 right after it.   But the size of every O  object is 0  so all objects o1,o2,o3  will all be allocated the same address – 0.  However,  different objects must have different addresses  so  zeor sized classes cannot be permitted and a class must have  a minimal size of 1 byte.

what about the 3 extra bytes added to structure B once the Hi function became virtual?
In this case having a virtual function means  including a pointer to the virtual function table aka vftable.  The pointer is a hidden member of the object accounting for the 4 bytes.

Conclusion

The size and layout of  C++ objects  are  not alway intuitive.  Assuming the location of an individual data member from its supposed layout in memory is  asking for a slow, agonizing decent into insanity. On the other hand,  paying attention to alignment considerations is a way  to pack your objects more tightly in memory.  When you need every last byte.

2 thoughts on “sizeof: When The Whole is Greater Than The Sum Of Its Parts”

Leave a Reply

Your email address will not be published. Required fields are marked *