Chapter 11

Alignment

Type sizes, alignment boundaries, struct padding, and how to order members for efficiency.

In this chapter

  1. Type Sizes & Alignment Boundaries
  2. Struct Padding
  3. Ordering Members to Avoid Padding
  4. Packing with #pragma pack
  5. Empty Class Size
  6. PS4 Architecture Notes
1

Type Sizes & Alignment Boundaries

Every type has an alignment requirement — the address it lives at must be a multiple of its size. A short (2 bytes) must be at an even address; an int (4 bytes) must be at an address divisible by 4, etc.

TypeSizeNotes
char1 byte
short2 bytes (16 bits)
int4 bytes (32 bits)
long4 or 8 bytesPlatform-dependent; use int32_t/int64_t for guaranteed width
float4 bytesUses floating point representation
double8 bytes (64 bits)4-byte aligned on some Linux ABIs; uses floating point
pointer4 or 8 bytes4 bytes on 32-bit, 8 bytes on 64-bit
Prefer fixed-width integers (std::int32_t, std::int64_t, etc. from <cstdint>) over long and similar platform-dependent types when exact size matters.

Word Sizes

"32-bit" and "64-bit" refer to the register size, which determines pointer size and how much RAM is addressable (32-bit registers can only reference ~3.5 GB RAM).

Term32-bit64-bit
doubleword4 bytes8 bytes
word4 bytes8 bytes
halfword2 bytes4 bytes
byte1 byte1 byte

1 GB = 1000 MB, 1 MB = 1000 KB, 1 KB = 1000 B


2

Struct Padding

When the compiler lays out a struct in memory, it inserts invisible padding bytes between members to ensure each member is properly aligned. This can make a struct larger than the sum of its members.

// As written — appears to be 8 bytes
struct MixedData {
    char  Data1;   // 1 byte
    short Data2;   // 2 bytes
    int   Data3;   // 4 bytes
    char  Data4;   // 1 byte
};
// After compilation on a 32-bit machine — actually 12 bytes!
struct MixedData {
    char  Data1;        // 1 byte
    char  Padding1[1];  // 1 byte padding so Data2 is 2-byte aligned
    short Data2;        // 2 bytes
    int   Data3;        // 4 bytes
    char  Data4;        // 1 byte
    char  Padding2[3];  // 3 bytes tail padding so struct size is multiple of 4
};
The struct itself doesn't have a single alignment requirement — it aligns each member individually. But the overall size will always be a multiple of the largest member's alignment, so that arrays of the struct are correctly aligned.

3

Ordering Members to Avoid Padding

Declaring members in decreasing size order eliminates all internal padding because each member is already naturally aligned by the time it's placed:

✗ Poor order — 12 bytes
struct MixedData {
    char  Data1;
    short Data2;
    int   Data3;
    char  Data4;
};
✓ Optimal order — 8 bytes
struct MixedData {
    int   Data3;  // largest
    short Data2;  // medium
    char  Data1;  // small
    char  Data4;  // small
};
In cache-sensitive code (game engines, real-time systems), struct layout directly impacts performance. Accessing a hot struct that spans two cache lines is significantly slower than one that fits in one. Order your members by size and group frequently-accessed members together.

4

Packing with #pragma pack

To force the compiler to use 1-byte alignment (no padding at all), wrap the struct in #pragma pack directives:

#pragma pack(push, 1)
struct PackedData {
    char  a;
    int   b;
    short c;
};
#pragma pack(pop)
Packed structs can hurt performance — unaligned memory access is slower on most architectures, and on some (older ARM) it causes a hardware fault. Only use packing when binary layout must exactly match an external format (network packets, file headers, hardware registers).

5

Empty Class Size

An empty class or struct has a size of at least 1 byte — never 0.

class Empty {};
sizeof(Empty);  // 1 (not 0)

This is required so that every object has a unique address — two distinct Empty objects must not share the same address, which a size of 0 would make impossible.

The Empty Base Optimization (EBO) is an exception: if an empty class is used as a base class, the compiler may collapse its size to 0 within the derived class. std::unique_ptr exploits this to store a deleter with no overhead.

6

PS4 Architecture Notes

A real-world example of why alignment and cache behavior matters in game development:

PropertyValue
CPUs8 × Jaguar cores (x86-64, not ARM)
Bus width32 bytes
Cache line size64 bytes (a full cache line is "hot data")
L2 hit (same cluster)~26 cycles
L2 miss (other cluster)~190 cycles
Main RAM access26 cycles + ~100 ms latency
The ~7× cycle cost difference between an L2 hit and an L2 miss illustrates exactly why cache-friendly struct layout matters. Keeping frequently-accessed members packed together in the first 64 bytes of a struct can be the difference between a smooth 60fps and a stuttering game.
← Chapter 10 ↑ Index Chapter 12 →