This article is authored by: Wang Fanghao, edited by: Apollo.
The following article is provided by Community Evangelist – Wang Fanghao, and it mainly introduces C++ Memory Alignment.

-
Why memory alignment? -
Side effects of memory alignment -
Space waste -
Compilation ABI issues -
How to align memory -
Stack memory -
Heap memory -
Conclusion
Why Memory Alignment?
struct example { char a; int b;};

Side Effects of Memory Alignment
// size 12struct one {char a;int b;short c;};// size 8struct two {int b;char a;short c;};
Thus, maintaining a good variable declaration order can save space, but it also brings additional issues. The initialization of the class is done in the order of variable declaration, so changing the declaration order of variables also requires attention to whether the initialization order is affected.
2. Compilation ABI Issues
Using the gcc compilation option -march=native can enable the X86 SSE, AVX instruction sets, or ARM neon instruction sets. If a dynamic library enables the above options, but the caller does not enable them, or vice versa, it will lead to errors. The gcc compiler cannot check the compilation options of the dynamic library at that time, leading to binary incompatibility issues. The solution is simple: recompile both sides to either support memory alignment or not support it.
How to Align Memory
// Specify 16-byte alignmentstruct alignas(16) example { char a; int b;} A;
Stack memory is specified by the compiler. Through pre-compilation options, we can inform the compiler of the required alignment in bytes. In other words, for stack memory, we only need to specify the required alignment in bytes using keywords, and the compiler will automatically align it. For built-in types in C++, such as char, int, float, double, etc., there is no need to specify the alignment in bytes; the compiler will align based on the type by default.
Function Parameters
However, function parameters are an exception. If function parameters require memory alignment, do not pass them by value; instead, use references or pointers.
// Wrongvoid func(A a, int b) {}// Correctvoid func(const A* a, int b) {}void func(const A& a, int b) {}
Function parameters can actually be considered as stack memory. Why can’t we pass by value? In fact, function parameter passing can be understood as making a copy of the parameters before passing them and then calling the function. The entry address of the function is fixed, so if the entry address is not aligned, it can lead to misalignment of parameters, resulting in exceptions.
void func() { A a = a; int b = b;}
C++20 introduced assume_aligned to solve the above problems.
2. Heap Memory
Stack memory is aligned using the keywords alignas and alignof, while heap memory is relatively more complicated because heap memory is returned by malloc. Let’s look at the following example.
struct bar { A a; int b;} BAR;BAR b; // Memory alignmentBAR* b = new BAR(); // Memory error
A is aligned to 16 bytes, so declaring the variable allocates stack memory, and the compiler will automatically align it to 16 bytes. However, if the object is created with new, it is not allocated by the compiler but dynamically allocated by new, and ordinary alloc does not guarantee byte alignment. If alignment is needed, we can only reconstruct the new method of BAR to use aligned_alloc to allocate aligned memory.
In fact, C++17 provides aligned_alloc for aligned memory allocation, so if you are using C++17 or later, you don’t need to worry about the above issues.
Conclusion


