Skip to main content

Struct Layout, Padding, and Alignment

What This Concept Is

A struct is a named sequence of fields laid out consecutively in memory. Three rules govern the layout:

  • fields appear in declaration order
  • each field is placed at an offset that is a multiple of its alignment requirement (usually its size for primitives: 1, 2, 4, 8 bytes)
  • the struct's own alignment is the maximum of its fields' alignments, and its total size is padded up to a multiple of that alignment so that arrays of the struct work

The gaps inserted to satisfy these rules are padding. Padding holds unspecified bytes; you may not rely on them.

Why It Matters Here

Struct layout drives:

  • sizeof surprises (sizeof(S) != sum of field sizes)
  • cache-line efficiency (narrower structs fit more per line)
  • wire / file formats (where exact bytes matter)
  • ABI compatibility across compilers and architectures

A one-field reorder can change sizeof by a factor of two.

Concrete Example

On x86-64 with default alignment:

struct A {        /* sizeof(struct A) = 24 */
char c; /* offset 0, size 1 */
/* padding 0..7 (7 bytes) so next field is 8-aligned */
double d; /* offset 8, size 8 */
int i; /* offset 16, size 4 */
/* trailing padding so sizeof is a multiple of 8 */
}; /* total 24: c(1) pad(7) d(8) i(4) pad(4) */

struct B { /* sizeof(struct B) = 16 */
double d; /* offset 0, size 8 */
int i; /* offset 8, size 4 */
char c; /* offset 12, size 1 */
/* trailing padding 3 bytes (to align next array entry) */
}; /* total 16: d(8) i(4) c(1) pad(3) */

Memory picture of one struct A:

offset:  0   1   2   3   4   5   6   7   8       15  16      19  20      23
+---+---+---+---+---+---+---+---+----------+-----------+-----------+
content: | c | <---- padding (7) ----> | d | i | pad (4) |
+---+---+---+---+---+---+---+---+----------+-----------+-----------+

Ordering fields from largest to smallest alignment usually minimizes size.

Common Confusion / Misconception

"sizeof returns the sum of field sizes." No. It returns the padded total including alignment slack.

"Padding is zero." The values of padding bytes are unspecified. Do not read them; do not expect memcmp on two structs to say "equal" just because all named fields are equal.

"#pragma pack(1) just saves memory." It also removes alignment guarantees. On architectures that trap on misaligned accesses (some ARM configurations, older SPARC), packed-struct field accesses can raise SIGBUS or be an order of magnitude slower.

How To Use It

For every struct you declare:

  1. Reorder fields from largest alignment to smallest to minimize padding.
  2. Use offsetof(struct S, field) and sizeof(struct S) in static assertions when layout matters.
  3. Only use __attribute__((packed)) / #pragma pack when a wire format demands it, and read the fields through small-unit accessors that tolerate misalignment.
  4. Access fields through the struct type (s.field, p->field); do not compute offsets by hand unless you have to.

Check Yourself

  1. Compute sizeof for struct { char c; int i; char d; }; on x86-64. Which fields are at which offsets?
  2. Why does reordering fields often change sizeof but never the total size of named data?
  3. What does offsetof(S, x) return, and where is it defined?

Mini Drill or Application

#include <stdio.h>
#include <stddef.h>

struct A { char c; double d; int i; };
struct B { double d; int i; char c; };

#define P(T, f) printf("%-10s offset %zu\n", #f, offsetof(T, f))

int main(void) {
printf("sizeof A = %zu\n", sizeof(struct A));
P(struct A, c); P(struct A, d); P(struct A, i);
printf("sizeof B = %zu\n", sizeof(struct B));
P(struct B, d); P(struct B, i); P(struct B, c);
return 0;
}

Build: gcc -Wall -Wextra -o layout layout.c. Predict sizes and offsets first. Rearrange field orders and redo. With -Wpadded, the compiler will warn where it is inserting padding.

Read This Only If Stuck