07 / 163 · C++11 · 8 min
Alignment: memory alignment and struct padding
Alignment states the address conditions under which an object may be placed, and sizeof includes the padding required for a valid layout. alignof queries a type's requirement, and alignas raises the alignment requirement of a declaration. Adding member sizes cannot replace layout calculation, and a packed struct cannot replace portable serialization.
In this lesson
What size and alignment each answer
Size answers how many bytes a complete object occupies. Alignment answers which addresses that object may occupy. alignof(T) gives the alignment requirement of T. That number is chosen by the implementation; you must not memorize both the size and the alignment of double as eight on every platform. An address that satisfies a stricter alignment also satisfies a weaker valid alignment requirement, so a more strongly aligned location remains a legal place for a less demanding type.
A struct must place every member and still be able to sit in an array of consecutive elements. Internal padding may appear between members so that a later member meets its own alignment. Trailing padding may appear at the end so that the next array element of the same struct type is also correctly aligned. For an ordinary complete object, the size must support that array stride; therefore sizeof is not a simple sum of member sizes. Layout is a property of the whole type, not a running total you can finish in your head from the member list alone.
Query first, then request a requirement
alignas(T) makes a declaration satisfy at least the alignment of T. alignas(value) requests that specified extended alignment. The request must be supported by the target implementation, and it must not lower the alignment the object already needs. The example uses the known type requirement of double to raise the alignment of a small struct. It does not assume every platform supports some huge numeric alignment value, and it does not treat an arbitrary integer as a portable request.
Reordering members can sometimes shrink gaps, but it changes layout, initialization order, and possibly ABI. You cannot rearrange public data formats at will just because a tighter packing looks attractive on one compiler. Whether reordering is worth it should be measured on the actual target, not treated as a standard theorem derived from hand calculation of one ABI. Query with alignof and sizeof, then request extra alignment only when you have a reason the implementation can honor.
Alignment is not object lifetime
Raw storage that is large enough and correctly aligned does not automatically let you interpret it as an arbitrary class object. Object lifetime and the type of a legitimate access must also be satisfied. Casting an unaligned byte address to int* and reading cannot be washed away by "the CPU allows unaligned access"; that is still a language-level error even on hardware that would not trap.
When handling file or network data, prefer reading fields according to the protocol, or use memcpy for a trivially copyable type that meets the requirements. Do not emit every byte of a struct as a portable format. Besides endianness, padding, member representation, and compiler ABI may differ. Padding may also carry leftover contents that should not be published. Alignment tells you where an object may live; it does not turn a byte buffer into that object, and it does not make a native struct layout into a wire format.
Pitfalls
- #pragma pack is an implementation-specific mechanism and may create unaligned members; it does not guarantee a cross-compiler protocol.
- memcmp on every byte of a struct is not the same as comparing the semantic values of the members; padding may differ.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <iostream>
struct Record {
char tag;
int value;
};
struct alignas(double) AlignedTag {
char tag;
};
int main() {
static_assert(alignof(Record) >= alignof(int), "member alignment");
static_assert(sizeof(Record) % alignof(Record) == 0, "array stride");
static_assert(alignof(AlignedTag) >= alignof(double), "requested alignment");
Record items[2] = {{'a', 10}, {'b', 20}};
assert(items[1].value == 20);
std::cout << items[0].tag << ' ' << items[1].value << '\n';
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-alignment.cpp -o example && ./exampleExpected result
a 20
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
Why can you not assert sizeof(S) == 5 for struct S { char c; int x; }; from the member declarations alone? What target information do you need?
Show a reference answer
First, int is not fixed by the standard at four bytes. Second, padding may be required before x, and the whole S may also be padded at the end. You need the target ABI's sizeof(int), alignof(int), struct layout rules, and any related packing options. A reliable program queries sizeof(S); a serialization protocol should specify field encodings explicitly and must not depend on the in-memory layout of S.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.