C fundamentals · lesson 04

Struct alignment and padding

You define a struct with 14 bytes of fields. <code>sizeof</code> says 24. Where did the extra 10 bytes come from?

in progress
10 min interactive

The hardware demands alignment

On x86, CPUs can read misaligned data — but it's slow. On ARM, misaligned reads crash the program. So the compiler enforces a rule: every object must be stored at an address that's a multiple of its size. A 4-byte int must start at an address divisible by 4. An 8-byte double must start at an address divisible by 8.

When you mix types of different sizes in a struct, the compiler inserts padding bytes between fields to satisfy this rule. Those bytes are real memory — they waste space and affect sizeof. You're paying for them whether you know it or not.

💡
Alignment rule: a type of size N must start at an address that's a multiple of N (up to 8 bytes). The compiler automatically inserts padding to make this happen. You cannot prevent it — but you can reduce it by ordering fields carefully.

A badly ordered struct

c
struct Bad { char a; // 1 byte at offset 0 int b; // 4 bytes — must be at offset 4, so 3 bytes of padding inserted char c; // 1 byte at offset 8 double d; // 8 bytes — must be at offset 16, so 7 bytes of padding inserted }; // Fields sum to 14 bytes. sizeof(Bad) = 24. Padding = 10 bytes (42% overhead). printf("%zu\n", sizeof(struct Bad)); // 24

The fix: sort fields largest to smallest

c
struct Good { double d; // 8 bytes at offset 0 int b; // 4 bytes at offset 8 char a; // 1 byte at offset 12 char c; // 1 byte at offset 13 // compiler adds 2 tail bytes so sizeof is a multiple of 8 }; // sizeof(Good) = 16. Same data, 33% smaller. printf("%zu\n", sizeof(struct Good)); // 16
Rule: declare struct fields from largest to smallest. 8-byte types first, then 4-byte, then 2-byte, then 1-byte. Group same-size fields together. This minimizes internal padding.

Why the tail also gets padded

The compiler also adds padding at the end of a struct so that the total size is a multiple of the struct's largest alignment. This matters when you have arrays of structs: every element in struct Good arr[100] must be properly aligned. Without tail padding, the second element in the array would start at an unaligned address.

See the bytes

The visualization below shows three scenarios: the bad struct, the optimized struct, and an array of ints. Click any byte to see exactly what it is and why it's there. Switch between scenarios with the tabs or the prev/next buttons.

struct layout — byte by byte
Click a tab to switch scenarios. Click any byte to see its meaning.
⚠️
Don't use __attribute__((packed)) to remove padding. It removes alignment guarantees. Reads from packed structs may be slower (or crash on ARM). The right answer is to reorder your fields.
one-line takeaway

Struct fields get padding bytes inserted between them for alignment. Sort largest to smallest to minimize waste.