Understanding Memory Alignment and Padding in C Structs

Calculating Member Offsets

To understand how structures are laid out in memory, it is useful to know the concept of an offset. The offset of a member is the distance in bytes from the start of the structure's base address. The first member always has an offset of 0.

You can utilize the standard macro offsetof (defined in stddef.h) to inspect these values programmatically:

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

struct Example {
    char a;
    int b;
    char c;
};

int main() {
    printf("Offset of a: %zu\n", offsetof(struct Example, a));
    printf("Offset of b: %zu\n", offsetof(struct Example, b));
    printf("Offset of c: %zu\n", offsetof(struct Example, c));
    return 0;
}

Rules of Memory Alignment

Structures are not simply packed tightly together. The copmiler inserts gaps (padding) based on specific alignment rules to optimize access speed:

  1. Initial Member: The first member is placed at offset 0.
  2. Subsequent Members: Each member must be placed at an address that is a multiple of its alignmant value.
    • Alignment Value = Min(Compiler Default Alignment, Size of Member).
    • Note: In Visual Studio, the default is usually 8. In GCC, there is no default; the alignment is simply the size of the member.
  3. Total Size: The total size of the structure must be a multiple of the largest alignment value found among its members.
  4. Nested Structures: If a structure contains another structure, the inner structure aligns to the alignment value of its own largest member. The outer structure's total size must then account for all alignment values (inner and outer).

Practical Calculation Examples

Example 1: Basic Padding

typedef struct {
    char ch;   // Size 1, Alignment 1
    int num;   // Size 4, Alignment 4
    char end;  // Size 1, Alignment 1
} Pack1;
  • ch starts at 0.
  • num requires alignment 4. It cannot start at 1; it starts at 4. (Bytes 1, 2, 3 are padding).
  • end starts at 8.
  • Current size is 9 bytes. The largest alignment is 4. 9 is not a multiple of 4, so the size rounds up to 12.

Example 2: Reordering Impact

typedef struct {
    char ch1;  // Size 1, Alignment 1
    char ch2;  // Size 1, Alignment 1
    int num;   // Size 4, Alignment 4
} Pack2;
  • ch1 at 0.
  • ch2 at 1.
  • num requires alignment 4. It starts at 4.
  • Current size is 8. The largest alignment is 4. 8 is a multiple of 4. Total size is 8.

Example 3: Larger Types

typedef struct {
    double val; // Size 8, Alignment 8
    char flag;  // Size 1, Alignment 1
    int id;     // Size 4, Alignment 4
} Pack3;
  • val starts at 0.
  • flag starts at 8.
  • id requires alignment 4. It starts at 12 (bytes 9, 10, 11 are padding).
  • Current size is 16. Largest alignment is 8. 16 is a multiple of 8. Total size is 16.

Example 4: Nested Structures

typedef struct {
    double val; 
    char flag; 
    int id;    
} Inner;

typedef struct {
    char start;   
    Inner data;  
    double last; 
} Outer;
  • start at 0.
  • data (Inner) has a largest member alignment of 8. It must start at offset 8.
  • Inside data (size 16), it occupies 8–23.
  • last (double, alignment 8) starts at 24.
  • Current size is 32. Largest alignment is 8. Total size is 32.

Why Align Memory?

Memory alignment is a trade-off between memory usage and performance:

  1. Hardware Requirements: Some CPU architectures cannot access arbitrary addresses. Attempting to read a 4-byte integer from an address that isn't a multiple of 4 may cause a hardware fault or bus error.
  2. Performance Optimization: Even on architectures that support unaligned access (like x86), it is slower. If a data element straddles two memory blocks (cache lines), the processor must perform two fetch operations instead of one. Aligning data ensures single-cycle access.

Tags: c programming Structures Memory Alignment System Programming Data Padding

Posted on Fri, 08 May 2026 22:44:06 +0000 by rathersurf