For systems programmers working close to the hardware, memory layout is a crucial factor in designing efficient and correct programs. Poor memory alignment leads to hard-to-find bugs, performance degradation, and sometimes outright crashes — especially in embedded or cross-platform contexts.
This article explores the core principles of memory alignment and padding in C++, explains why misalignment hurts, and shows how to optimize struct layout safely.
What Is Memory Alignment?
Memory alignment refers to placing data at memory addresses that are multiples of the data’s size or required alignment.
- On a 32-bit system:
- Data is typically accessed in 4-byte words
- Types like
intandfloat(4 bytes) must be stored at addresses divisible by 4
- On a 64-bit system:
- Many types (like
doubleor pointers) align to 8 bytes
- Many types (like
Why Alignment Matters
1. CPU Efficiency
Aligned access lets the CPU fetch data in a single operation. Unaligned access can cause:
- Multiple memory fetches
- Bit shifting and recombination
- Stall cycles
2. Hardware Restrictions
Some architectures (like ARM, SPARC, MIPS) do not allow unaligned access at all:
- Accessing an
intat a non-4-byte-aligned address may cause a hardware exception
3. Data Corruption & Crashes
Casting misaligned memory into structs or pointers can lead to:
- Corrupted reads/writes
- Cache line fragmentation
- Segmentation faults
Misaligned Scenario
Struct without padding (assuming no alignment):
struct BadLayout {
bool b; // offset 0
int i; // offset 1 (misaligned!)
float f; // offset 5 (also misaligned!)
};
On a 32-bit system:
intandfloatmust be aligned to 4-byte boundaries (offsets 0, 4, 8, etc.)- In this layout:
intstarts at offset 1 → misalignedfloatstarts at offset 5 → misaligned
boolis fine at offset 0
Let’s say the CPU wants to read i at offset 1 (misaligned 4-byte access):
❌ Unaligned Read (int at offset 1):
- CPU needs two memory fetches:
- First: read from address 0–3
- Second: read from address 4–7
- Then it reconstructs the value by combining the relevant bytes
This is:
- Slower
- Can cause a hardware exception on strict CPUs (e.g., ARM)
- May even result in incorrect data if you're casting
So to save the CPU cycles, and to optimize, compiler adds the padding the align the members.
What is Padding?
Padding refers to extra, unused bytes inserted by the compiler to satisfy alignment constraints.
Example:
struct Misaligned {
bool b; // offset 0
int i; // offset 1 (!!! misaligned)
float f; // offset 5 (!!! also misaligned)
};
On a 32-bit system:
intandfloatmust be aligned to 4-byte boundaries- Since
iis at offset 1, it's misaligned
How Compilers Fix This: Padding
To fix the above struct, compilers insert padding bytes to restore alignment.
struct Aligned {
bool b; // offset 0
char pad[3]; // offset 1–3 (compiler-added padding)
int i; // offset 4
float f; // offset 8
};
Now:
iandfare 4-byte aligned- Struct size is 12 bytes, but only 9 bytes are used
- The rest is padding
Example: Size of structure
#include <iostream>
struct S {
char c;
int i;
};
int main() {
std::cout << "Size of S: " << sizeof(S) << "\n";
std::cout << "Alignment of S: " << alignof(S) << "\n";
}Output:
Size of S: 8
Alignment of S: 4Disable Padding
1️⃣ GCC/ Clang Attribute
In GCC and clang__attribute__((packed)) is used as extension to disable the padding.
It tells the compiler to remove all padding between members of a struct/class.
That means:
- Members are placed back-to-back
- Alignment rules are not enforced
- Structure becomes compact in memory
Example:
#include <iostream>
struct S {
char c;
int i;
}__attribute__((packed));
int main() {
std::cout << "Size of S: " << sizeof(S) << "\n";
std::cout << "Alignment of S: " << alignof(S) << "\n";
}
Output:
Size of S: 5
Alignment of S: 12️⃣ Use #pragma pack (Compiler-Specific)
#pragma pack(push, 1)
struct Packed {
char a;
int b;
char c;
};
#pragma pack(pop)
Forces 1-byte alignment, output Size = 6 bytes.
But can lead to performance penalties or unsafe memory access.
3️⃣ Reorder Members
Place the larger members first.
struct PackedBetter {
int b;
char a;
char c;
};
Now size becomes 8 bytes instead of 12. Always place larger members first.
⚠️ Dangers of packed
| Risk | Description |
|---|---|
| Unaligned access | May crash on hardware like ARM, MIPS, or SPARC |
| Slow memory fetch | Unaligned reads take 2x+ more cycles on some CPUs |
| Undefined behavior | Casting pointers to misaligned structs is risky |
| Compiler-specific | Not standard C++, may need porting effort |
Nested Structural Padding
#include <iostream>
using namespace std;
struct A {
int x;
double d;
};
struct B {
int y;
A a;
};
int main() {
cout << sizeof(B) << endl;
}Consider the following example:
First, Understand Alignment (64-bit system)
| Type | Size | Alignment requirement |
|---|---|---|
int | 4 | 4 bytes |
double | 8 | 8 bytes |
This means:
intcan go at any address divisible by 4doublemust go at an address divisible by 8
Why struct A has padding:
struct A {
int x; // 4 bytes
double d; // 8 bytes (must be aligned to 8-byte boundary)
};int xstarts at offset 0double dmust start at an address divisible by 8- But after
x, the next byte is offset 4 - So, the compiler inserts 4 bytes of padding between
xandd. - Then
dstarts at offset 8
👉 So, sizeof(A) = 4 (x) + 4 (padding) + 8 (d) = 16 bytes
Layout of A:
| Offset | Member | Size | Note |
|---|---|---|---|
| 0 | x | 4 | int x |
| 4–7 | padding | 4 | needed to align double d at 8 |
| 8–15 | d | 8 | double d |
Why struct B has more padding:
struct B {
int y; // 4 bytes
A a; // 16 bytes (but needs to start on an 8-byte boundary)
};
int ystarts at offset 0- After
y, we're at offset 4 Acontains adouble, so it must be aligned on an 8-byte boundary- But offset 4 is not divisible by 8, so the compiler adds 4 more bytes of padding
- Then
Astarts at offset 8 and takes 16 bytes
👉 So, sizeof(B) = 4 (y) + 4 (padding) + 16 (a) = 24 bytes
Layout of B:
| Offset | Member | Size | Note |
|---|---|---|---|
| 0 | y | 4 | int y |
| 4–7 | padding | 4 | needed to align a (because a has a double inside) |
| 8–23 | a | 16 | from 8 to 23 |
Why all this?
To make sure every
double(and structs that contain doubles) starts on an 8-byte aligned address, which is required by the CPU for fast and correct access.
Conclusion
Alignment Rules Vary by Architecture
- Most 32-bit and 64-bit systems align data to its size (e.g.,
intto 4,doubleto 8). - On some embedded systems or special compilers, the rules may differ.
Padding Affects Performance and Memory
- More padding = more memory waste, especially in arrays of structs.
- But removing padding (e.g., with
#pragma pack) can hurt performance or crash programs on some CPUs due to misaligned memory access.
Reordering Members Reduces Padding
To minimize padding:
Place larger data types first, then smaller ones.
Example:
struct Bad {
char c;
int i;
}; // May use 8 bytes (1 + 3 padding + 4)
struct Better {
int i;
char c;
}; // Only 5 bytes needed, may use 8 still due to final padding, but no *internal* padding
Inspect Layout with offsetof
You can inspect the actual byte offsets:
#include <iostream>
#include <cstddef>
struct A {
char c;
int i;
};
int main() {
std::cout << "Offset of c: " << offsetof(A, c) << "\n";
std::cout << "Offset of i: " << offsetof(A, i) << "\n";
std::cout << "Size of A: " << sizeof(A) << "\n";
}
Packing Structs (with Caution)
You can force smaller size with:
GCC/Clang:
struct __attribute__((packed)) A { ... };MSVC:
#pragma pack(push, 1) struct A { ... }; #pragma pack(pop)
⚠️ BUT: Packed structs may cause:
- Slower memory access
- Crashes on some CPUs
- Issues with memory-mapped I/O or serialization
Use only when absolutely needed, like in:
- File/network protocols
- Embedded systems
- Bit-level hardware control
Memory Alignment with alignas
C++11 and newer lets you control alignment:
struct alignas(16) AlignedStruct {
int x;
};
Use this when you need specific alignment for SIMD, hardware, or performance tuning
Tools for Padding Inspection
sizeof()– tells total sizeoffsetof()– tells position of each member- Linux
paholetool – shows struct layout & padding (very useful!) - Static analysis tools – e.g., Clang Analyzer, GCC warnings
Leave a comment
Your email address will not be published. Required fields are marked *
