C Alignment & _Alignof (C11)

Memory alignment and alignment queries in C

📐 What is Memory Alignment?

Memory alignment ensures data is stored at memory addresses that are multiples of the data size. This improves performance and prevents crashes on some architectures.


#include <stdio.h>
#include <stdalign.h>

int main() {
    printf("int alignment: %zu\n", _Alignof(int));
    printf("double alignment: %zu\n", _Alignof(double));
    return 0;
}
                                    

Alignment Concepts

🎯

_Alignof

Query alignment requirements

size_t align = _Alignof(int);
📏

_Alignas

Specify custom alignment

_Alignas(16) int x;
🏗️

aligned_alloc

Allocate aligned memory

void* ptr = aligned_alloc(16, 64);
📊

max_align_t

Maximum alignment type

_Alignof(max_align_t)

🔹 Checking Alignment

Use _Alignof operator to determine memory alignment requirements for data types. Alignment specifies memory address boundaries where variables must be stored for optimal access. Different data types have different alignment requirements: chars typically need 1-byte alignment while doubles need 8-byte alignment. The _Alignof(type) expression returns the required alignment in bytes. Understanding alignment helps optimize memory layout, improve cache efficiency, and ensure portable code across different platforms.

#include <stdio.h>
#include <stdalign.h>

struct MyStruct {
    char c;
    int i;
    double d;
};

int main() {
    printf("char alignment: %zu bytes\n", _Alignof(char));
    printf("int alignment: %zu bytes\n", _Alignof(int));
    printf("double alignment: %zu bytes\n", _Alignof(double));
    printf("struct alignment: %zu bytes\n", _Alignof(struct MyStruct));
    printf("max_align_t: %zu bytes\n", _Alignof(max_align_t));
    
    return 0;
}

Output (typical x64):

char alignment: 1 bytes
int alignment: 4 bytes
double alignment: 8 bytes
struct alignment: 8 bytes
max_align_t: 16 bytes

🔹 Custom Alignment

The _Alignas specifier enforces custom alignment for variables and structure members. Use _Alignas(alignment) to force specific byte alignment requirements, useful when interfacing with hardware or external libraries. This specifier overrides default alignment rules, ensuring variables occupy memory positions matching hardware expectations. Custom alignment becomes essential when working with aligned SIMD operations or memory-mapped I/O structures requiring precise positioning.

#include <stdio.h>
#include <stdalign.h>

int main() {
    // Normal alignment
    int normal_int;
    
    // Custom 16-byte alignment
    _Alignas(16) int aligned_int;
    
    // Align to double's requirement
    _Alignas(double) int double_aligned;
    
    printf("Normal int address: %p\n", (void*)&normal_int);
    printf("16-byte aligned: %p\n", (void*)&aligned_int);
    printf("Double aligned: %p\n", (void*)&double_aligned);
    
    // Check if addresses are properly aligned
    printf("16-byte aligned check: %s\n", 
           ((uintptr_t)&aligned_int % 16 == 0) ? "Yes" : "No");
    
    return 0;
}

Output (example):

Normal int address: 0x7fff5fbff6ac
16-byte aligned: 0x7fff5fbff6b0
Double aligned: 0x7fff5fbff6b8
16-byte aligned check: Yes

🔹 Aligned Memory Allocation

Allocate memory with specific alignment using specialized functions for performance-critical operations. Functions like memalign() and posix_memalign() allocate memory at precise boundaries required by modern processors. Properly aligned memory enables vectorized operations, cache-efficient access patterns, and hardware acceleration. This technique is crucial for high-performance computing, multimedia processing, and scientific applications. Understanding aligned allocation unlocks significant performance improvements in computationally intensive programs.

#include <stdio.h>
#include <stdlib.h>
#include <stdalign.h>

int main() {
    // Allocate 64 bytes with 16-byte alignment
    void* aligned_ptr = aligned_alloc(16, 64);
    
    if (aligned_ptr) {
        printf("Allocated address: %p\n", aligned_ptr);
        printf("16-byte aligned: %s\n", 
               ((uintptr_t)aligned_ptr % 16 == 0) ? "Yes" : "No");
        
        // Use the memory
        int* int_array = (int*)aligned_ptr;
        for (int i = 0; i < 16; i++) {
            int_array[i] = i * i;
        }
        
        printf("First few values: %d, %d, %d\n", 
               int_array[0], int_array[1], int_array[2]);
        
        free(aligned_ptr);
    } else {
        printf("Allocation failed\n");
    }
    
    return 0;
}

Output:

Allocated address: 0x55a8b2c01010
16-byte aligned: Yes
First few values: 0, 1, 4

🔹 Alignment in Structures

Structure padding and alignment affect memory layout, size, and data access efficiency. Compilers add padding bytes between structure members to maintain alignment requirements, increasing struct size. Reordering members from largest to smallest minimizes padding waste. Understanding padding helps optimize structure layouts for memory efficiency and cache performance. The sizeof() operator reveals actual struct sizes including padding. Strategic struct design improves both memory usage and program performance.

Structure Alignment Rules:

  • Member alignment: Each member aligns to its natural boundary
  • Structure alignment: Whole structure aligns to largest member
  • Padding: Compiler adds padding bytes for alignment
#include <stdio.h>
#include <stdalign.h>

struct Unaligned {
    char c;      // 1 byte
    int i;       // 4 bytes (3 bytes padding before)
    char c2;     // 1 byte (3 bytes padding after)
};

struct _Alignas(32) HighlyAligned {
    char c;
    int i;
};

int main() {
    printf("Unaligned struct size: %zu\n", sizeof(struct Unaligned));
    printf("Unaligned alignment: %zu\n", _Alignof(struct Unaligned));
    
    printf("Highly aligned size: %zu\n", sizeof(struct HighlyAligned));
    printf("Highly aligned alignment: %zu\n", _Alignof(struct HighlyAligned));
    
    return 0;
}

Output:

Unaligned struct size: 12
Unaligned alignment: 4
Highly aligned size: 32
Highly aligned alignment: 32

🧠 Test Your Knowledge

What does _Alignof return?