Structure Padding in C++

Lately, I was discussing with other developers at my company about the memory alignment of structure members and I thought why not make a short post about it. To be honest I think it’s an underrated topic nowadays with machines with basically infinite memory and billions of cycles per second. Regardless, even if it’s not important for many developers, it’s still good to know and important if you develop on devices with limited resources.

Data Models and Resulting Sizes of Fundamental Types

The C++ standard is at least guarantying 16bit width for short, unsigned short, int, and unsigned int. 32bit width is guaranteed for long and unsigned long and 64bit for long long and unsigned long long. Eventually, the resulting bit size of the fundamental types is defined by the implemented data model. Four data models are widely-used, LP32, ILP32 on 32bit machines, and LLP64, LP64 on 64bit machines.

TypeStandardLP32ILP32LLP64LP64
short/unsigned short1616161616
int/unsigned int1616323232
long/unsigned long3232323264
long long/unsigned long long6464646464
Guaranteed bit size and data model depended bit size, source cppreference.com

Let’s have a look at the bit size of some fundamental types of a specific implementation on a Ubuntu Linux with clang 7 compiler.

Size of char = sizeof(char) = 1 byte -> 8bit
Size of short = sizeof(short) = 2 byte -> 16bit
Size of int = sizeof(int) = 4 byte -> 32bit
Size of long = sizeof(long) = 8 byte -> 64bit

Size of Structures

Let’s assume we have the following c++ structure.

struct S1 {
  char a;
  int b;
  char c;
  short d;
};

Then we would apparently assume the size of the structure in memory will be 8 byte. Let’s check this:

Size of S1 = 1 + 4 + 1 + 2 = 8 byte, real size is 
sizeof(S1) = 12 byte

That’s interesting. Why is the size of S1 12 byte instead of the expected 8 byte? Tho find an answer we have to dig a little bit deeper into how memory is managed by processors. Roughly explained a processor is capable of transferring 1 word (can be 4 bytes on 32bit and 8 bytes on 64bit machines) to and from memory in one cycle, and an element of fundamental type can be stored at multiples of its byte size in memory (depending on the used compiler). Let’s call it 1/2/4 Rule…

  • Types with a size of 1 Byte can be stored at multiple of 1 Byte
  • Types with a size of 2 Byte can be stored at multiple of 2 Byte
  • Types with a size of 4 Byte can be stored at multiple of 4 Byte
  • and so on

As a result, the memory alignment of our structure S1 would probably look like the following in memory with memory address from 0 to f and the resulting size of 12 bytes with 4 bytes of empty memory. This is called padding. The print out of the real memory address (first byte of the allocated memory of an element) is confirming it.

Memory Adress:0123456789abcdef
Allocated Memory:abbbbcdd
Memory alignment of structure S1 which results in a size of 12 byte
Address a: 0x7ffe2b97da30
Address b: 0x7ffe2b97da34
Address c: 0x7ffe2b97da38
Address d: 0x7ffe2b97da3a

Now it might strike you that it would probably possible to save memory by simply rearranging structure members of S1 and leveraging how the compiler is aligning elements in memory.

struct S2 {
  char a;
  char c;
  short d;
  int b;
};
Size of S2 = 1 + 1 + 2 + 4 = 8 byte, real size is 
sizeof(S2) = 8 byte
Address a: 0x7fff3ae69ab8
Address c: 0x7fff3ae69ab9
Address d: 0x7fff3ae69aba
Address b: 0x7fff3ae69abc
Memory Adress:0123456789abcdef
Allocated Memory:acddbbbb
Memory alignment of structure S2 which results in a size of 8 byte

This is much better now. But be aware, it’s not recommended to just reorder structure elements in ascending order (smallest to the largest element). It is always necessary to keep in mind what we called earlier the 1/2/4 rule. What is the size of S3? Does it have the size of 5 bytes?

struct S3 {
  char a;
  int b; 
};
Size of S3 = 1 + 4 = 5 byte, real size is 
sizeof(S3) = 8 byte

Again, keep in mind the 1/2/4 rule. Because of this, the memory alignment of the elements look now like this:

Address a: 0x7ffe2e8dc7a8
Address b: 0x7ffe2e8dc7ac
Memory Adress:0123456789abcdef
Allocated Memory:abbbb
Memory alignment of structure S3 which results in a size of 8 byte

The full source code of this example:

#include <iostream>
void printTypeSize() {
std::cout << "Size of char = " << sizeof(char) << " byte" << std:: endl;
std::cout << "Size of short = " << sizeof(short) << " byte" << std:: endl;
std::cout << "Size of int = " << sizeof(int) << " byte" << std:: endl;
std::cout << "Size of long = " << sizeof(long) << " byte" << std:: endl;
}
struct S1 {
char a; // 1
int b; // 4
char c; // 1
short d; // 2
};
void printS1() {
std::cout << "Size of S1 = 1 + 4 + 1 + 2 = "
<< sizeof(char) + sizeof(int) + sizeof(char) + sizeof(short) << " byte, real size is "
<< sizeof(S1) << " byte" << std:: endl;
}
void printS1MemoryLayout() {
S1 s;
std::cout << "Address a: " << (void*)&s.a << std::endl;
std::cout << "Address b: " << (void*)&s.b << std::endl;
std::cout << "Address c: " << (void*)&s.c << std::endl;
std::cout << "Address d: " << (void*)&s.d << std::endl;
}
struct S2 {
char a; // 1
char c; // 1
short d; // 2
int b; // 4
};
void printS2() {
std::cout << "Size of S2 = 1 + 1 + 2 + 4 = "
<< sizeof(char) + sizeof(int) + sizeof(char) + sizeof(short) << " byte, real size is "
<< sizeof(S2) << " byte" << std:: endl;
}
void printS2MemoryLayout() {
S2 s;
std::cout << "Address a: " << (void*)&s.a << std::endl;
std::cout << "Address c: " << (void*)&s.c << std::endl;
std::cout << "Address d: " << (void*)&s.d << std::endl;
std::cout << "Address b: " << (void*)&s.b << std::endl;
}
struct S3 {
char a;
int b;
};
void printS3() {
std::cout << "Size of S3 = 1 + 4 = "
<< sizeof(char) + sizeof(int) << " byte, real size is "
<< sizeof(S3) << " byte" << std:: endl;
}
void printS3MemoryLayout() {
S3 s;
std::cout << "Address a: " << (void*)&s.a << std::endl;
std::cout << "Address b: " << (void*)&s.b << std::endl;
}
int main() {
printTypeSize();
printS1();
printS1MemoryLayout();
printS2();
printS2MemoryLayout();
printS3();
printS3MemoryLayout();
}
view raw structurepadding.cpp hosted with ❤ by GitHub

tl;dr

Nowadays micro-optimizations like the padding/memory alignment of structures might be irrelevant to many fields of software engineering. But still, I think it is an important topic in cases of limited resources and should be always known by every C++ developer in general. It’s good to know your memory.

Did you like the post?
What are your thoughts? Did you like the post?
Feel free to comment and share this post.

Processing…
Success! You’re on the list.

2 thoughts on “Structure Padding in C++

  1. The information is somewhat correct (and probably was correct on older processors),but number of cycles and assuming that the word size is the automic memory fetch is WRONG on modern processors. The processors cycle faster than the memory fetch time, so it can take several cycles for a memory fetch to become available. And many fetch an entire cache line which may be multiple words.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.