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
int
andfloat
(4 bytes) must be stored at addresses divisible by 4
- On a 64-bit system:
- Many types (like
double
or 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
int
at 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:
int
andfloat
must be aligned to 4-byte boundaries (offsets 0, 4, 8, etc.)- In this layout:
int
starts at offset 1 → misalignedfloat
starts at offset 5 → misaligned
bool
is 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:
int
andfloat
must be aligned to 4-byte boundaries- Since
i
is 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:
i
andf
are 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: 4
Disable Padding
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: 1
⚠️ 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 |