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