Memory Alignment & Padding
Understand why compilers add padding to structures. Learn about memory alignment requirements, how to minimize padding, and packed structures for embedded systems.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Understand why memory alignment matters
- ✓Calculate structure padding and size
- ✓Optimize structure layout to reduce padding
- ✓Use packed structures when needed
- ✓Apply alignof and alignas (C11)
01Introduction
Have you ever noticed that the size of a struct is sometimes larger than the sum of its members? This is because of memory alignment and padding. Understanding these concepts is crucial for writing memory-efficient code, especially in embedded systems and performance-critical applications.
1#include <stdio.h>23struct Example {4 char a; // 1 byte5 int b; // 4 bytes6 char c; // 1 byte7}; // Total: 6 bytes... right?89int main() {10 printf("Size of struct: %zu bytes\n", sizeof(struct Example));11 // Output: Size of struct: 12 bytes ← Why not 6?12 return 0;13}What You'll Learn
- • Why compilers add padding to structures
- • How to calculate structure size with padding
- • Techniques to minimize padding
- • When and how to use packed structures
02Why Alignment Matters
CPUs are designed to access memory most efficiently when data is aligned to certain boundaries. For example, a 4-byte int is most efficiently accessed when its memory address is divisible by 4.
Aligned Access (Fast)
→ Single memory read
Unaligned Access (Slow/Error)
→ Two reads + combine OR crash!
Alignment Requirements by Type
| Type | Size (typical) | Alignment | Valid Addresses |
|---|---|---|---|
char | 1 byte | 1 byte | Any address |
short | 2 bytes | 2 bytes | 0, 2, 4, 6, 8... |
int | 4 bytes | 4 bytes | 0, 4, 8, 12, 16... |
long | 8 bytes | 8 bytes | 0, 8, 16, 24... |
double | 8 bytes | 8 bytes | 0, 8, 16, 24... |
pointer | 4/8 bytes | 4/8 bytes | Depends on platform |
03Structure Padding Explained
When you define a struct, the compiler automatically inserts padding bytesto ensure each member is properly aligned. Let's visualize this:
1struct Example {2 char a; // 1 byte at offset 03 // 3 bytes padding (to align int to 4-byte boundary)4 int b; // 4 bytes at offset 45 char c; // 1 byte at offset 86 // 3 bytes padding (total size must be multiple of 4)7};89// Memory layout:10// Offset: 0 1 2 3 4 5 6 7 8 9 10 1111// [ a |PAD|PAD|PAD| b | b | b | b | c |PAD|PAD|PAD]12//13// Total: 12 bytes (not 6!)Two Types of Padding
- Internal Padding: Between members to align them
- Trailing Padding: At the end to make struct size a multiple of its largest alignment
04Calculating Structure Size
Use offsetof() to see where each member starts, and sizeof for total size:
1#include <stdio.h>2#include <stddef.h> // For offsetof34struct Example {5 char a;6 int b;7 char c;8};910struct Better {11 int b; // Largest first12 char a;13 char c;14}; // Members grouped to reduce padding1516int main() {17 printf("=== struct Example ===\n");18 printf("sizeof: %zu\n", sizeof(struct Example));19 printf("offsetof(a): %zu\n", offsetof(struct Example, a));20 printf("offsetof(b): %zu\n", offsetof(struct Example, b));21 printf("offsetof(c): %zu\n", offsetof(struct Example, c));22 23 printf("\n=== struct Better ===\n");24 printf("sizeof: %zu\n", sizeof(struct Better));25 printf("offsetof(b): %zu\n", offsetof(struct Better, b));26 printf("offsetof(a): %zu\n", offsetof(struct Better, a));27 printf("offsetof(c): %zu\n", offsetof(struct Better, c));28 29 return 0;30}Expected Output:
sizeof: 12
offsetof(a): 0
offsetof(b): 4
offsetof(c): 8
=== struct Better ===
sizeof: 8
offsetof(b): 0
offsetof(a): 4
offsetof(c): 5
Memory Savings!
By reordering members (largest to smallest), we reduced the struct from 12 bytes to 8 bytes — a 33% reduction! Imagine the savings with millions of instances.
05Optimizing Structure Layout
The golden rule for minimizing padding: order members from largest to smallest.
1// BAD: Mixed sizes = lots of padding2struct BadLayout {3 char a; // 1 byte + 7 padding4 double b; // 8 bytes5 char c; // 1 byte + 3 padding6 int d; // 4 bytes7 char e; // 1 byte + 7 padding8}; // Total: 32 bytes (only 15 used!)910// GOOD: Largest to smallest = minimal padding11struct GoodLayout {12 double b; // 8 bytes13 int d; // 4 bytes14 char a; // 1 byte15 char c; // 1 byte16 char e; // 1 byte + 1 padding17}; // Total: 16 bytes (15 used, 1 padding)1819// 50% memory savings!Optimization Rules
- 1. Sort by size: double/pointer → long → int → short → char
- 2. Group same-sized members together
- 3. Put arrays at the end when possible
- 4. Consider cache line alignment for performance-critical structs (64 bytes)
06Packed Structures
When you need to eliminate ALL padding (for binary protocols, file formats, or hardware registers), you can use compiler-specific attributes to create packed structures:
1#include <stdio.h>23// Regular struct with padding4struct Normal {5 char a;6 int b;7 char c;8}; // 12 bytes910// Packed struct - NO padding (GCC/Clang)11struct __attribute__((packed)) Packed {12 char a;13 int b;14 char c;15}; // 6 bytes1617// Alternative: #pragma pack (works on MSVC, GCC, Clang)18#pragma pack(push, 1) // Set alignment to 1 byte19struct PackedPragma {20 char a;21 int b;22 char c;23}; // 6 bytes24#pragma pack(pop) // Restore previous alignment2526int main() {27 printf("Normal: %zu bytes\n", sizeof(struct Normal));28 printf("Packed: %zu bytes\n", sizeof(struct Packed));29 printf("PackedPragma: %zu bytes\n", sizeof(struct PackedPragma));30 return 0;31}Packed Structure Warnings
- • Performance penalty — Unaligned access is slower
- • Some CPUs crash — ARM in particular may fault on unaligned access
- • Not portable — Syntax varies by compiler
- • No pointer casting — Taking address of packed member is dangerous
07C11: alignof and alignas
C11 introduced standard ways to query and control alignment:
1#include <stdio.h>2#include <stdalign.h> // For alignof and alignas34int main() {5 // alignof: Query alignment requirement6 printf("Alignment of char: %zu\n", alignof(char)); // 17 printf("Alignment of int: %zu\n", alignof(int)); // 48 printf("Alignment of double: %zu\n", alignof(double));// 89 10 // alignas: Force specific alignment11 alignas(16) int aligned_array[4]; // 16-byte aligned12 alignas(64) char cache_line[64]; // Cache-line aligned13 14 printf("\nAddress of aligned_array: %p\n", (void*)aligned_array);15 printf("Address of cache_line: %p\n", (void*)cache_line);16 17 return 0;18}1920// Struct with custom alignment21struct alignas(32) AlignedStruct {22 int a;23 double b;24}; // Will be 32-byte aligned (and padded to 32 bytes)When to Use alignas
- • SIMD operations — SSE/AVX require 16/32-byte alignment
- • Cache optimization — Align to 64-byte cache lines
- • Memory-mapped hardware — Match hardware requirements
- • DMA buffers — Often need specific alignment
08Real-World Example: Network Packet
Packed structures are essential for network protocols where byte order and layout must match exactly:
1#include <stdio.h>2#include <stdint.h>3#include <arpa/inet.h> // For htons, htonl45// IP Header (simplified) - must be exactly 20 bytes6struct __attribute__((packed)) IPHeader {7 uint8_t version_ihl; // Version (4 bits) + IHL (4 bits)8 uint8_t tos; // Type of Service9 uint16_t total_length; // Total Length10 uint16_t identification; // Identification11 uint16_t flags_fragment; // Flags (3 bits) + Fragment Offset (13 bits)12 uint8_t ttl; // Time to Live13 uint8_t protocol; // Protocol14 uint16_t checksum; // Header Checksum15 uint32_t src_addr; // Source Address16 uint32_t dst_addr; // Destination Address17};1819_Static_assert(sizeof(struct IPHeader) == 20, "IP Header must be 20 bytes");2021void print_ip_header(struct IPHeader *hdr) {22 printf("Version: %d\n", (hdr->version_ihl >> 4) & 0x0F);23 printf("TTL: %d\n", hdr->ttl);24 printf("Protocol: %d\n", hdr->protocol);25 26 // Convert network byte order to host byte order27 uint32_t src = ntohl(hdr->src_addr);28 printf("Source IP: %d.%d.%d.%d\n",29 (src >> 24) & 0xFF, (src >> 16) & 0xFF,30 (src >> 8) & 0xFF, src & 0xFF);31}3233int main() {34 printf("Size of IPHeader: %zu bytes\n", sizeof(struct IPHeader));35 return 0;36}09Summary
Key Takeaways:
- ✓Alignment ensures data is at addresses divisible by its size
- ✓Padding is added by compilers to maintain alignment
- ✓Order struct members largest to smallest to minimize padding
- ✓Use
offsetof()to inspect member positions - ✓Use packed structures only when necessary (protocols, hardware)
- ✓C11 provides
alignofandalignasfor portable alignment control
Test Your Knowledge
Related Tutorials
C Program Memory Layout
See how C programs are organized in RAM! Learn about Stack, Heap, Data, BSS, and Text segments. Essential for understanding memory.
Endianness: Big-Endian vs Little-Endian
Understand byte order in multi-byte data. Learn the difference between big-endian and little-endian, how to detect endianness, and convert between formats for networking and file I/O.
Structures in C
Group related data together! Create your own data types with struct. Store a student's name, age, and grade in one variable.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!