How are arrays of structures laid out, and why does each element get padded even if its members fit tightly?
Each struct element is padded so its total size is a multiple of the struct's alignment, guaranteeing every element in the array starts properly aligned.
If a struct's size weren't a multiple of its alignment, then a[1], a[2], … would drift out of alignment as you index further into the array. So the compiler rounds the struct's size up, adding trailing padding:
struct S2 {
double v; // 8 bytes, needs 8-byte alignment
int i[2]; // 8 bytes
char c; // 1 byte
} a[10]; // each element is 24 bytes (not 17): pad to a multiple of 8
Layout of one element:
| v | i[0] | i[1] | c | 7 bytes pad |
+0 +8 +12 +16 +17 +24
So a[0] is at a+0, a[1] at a+24, a[2] at a+48 — element offset is 24*i.
Accessing a member of a[idx] combines both calculations — first scale the index by the element size, then add the member's in-struct offset:
# struct S3 { short i; float v; short j; } a[10]; (sizeof = 12, j at offset 8)
leal (%eax,%eax,2), %eax # eax = 3*idx (part of computing 12*idx)
movswl a+8(,%eax,4), %eax # load a[idx].j : base a + 8 (j's offset) + 12*idx
Takeaway: "size is a multiple of alignment" isn't pedantry — it's the rule that keeps every element of an array of structs correctly aligned.
Go deeper:
Data structure alignment (Wikipedia) — why each element's size is padded to a multiple of its alignment.