There are two basic rules that you need to follow:
Every instance of your structure must be located at a memory address which is divisible by the size of the largest field in the structure.
Each field in your structure must be located at an offset (within the structure) which is divisible by the size of that field itself.
For example, every instance of the following structure must reside in a memory address which is divisible by sizeof(uint32)
:
struct
{
uint16 a; // offset 0 (OK, because 0 is divisible by sizeof(uint16))
uint08 b; // offset 2 (OK, because 2 is divisible by sizeof(uint08))
uint08 c; // offset 3 (OK, because 3 is divisible by sizeof(uint08))
uint32 d; // offset 4 (OK, because 4 is divisible by sizeof(uint32))
}
Exceptions:
Rule #1 may be violated if the CPU architecture supports unaligned load and store operations. Nevertheless, such operations are usually less efficient (requiring the compiler to add NOPs "in between"). Ideally, one should strive to follow rule #1 even if the compiler does support unaligned operations, and let the compiler know that the data is well aligned (using a dedicated #pragma
), in order to allow the compiler to use aligned operations where possible.
Rule #2 may be violated if the compiler automatically generates the required padding.
This, of course, changes the size of each instance of the structure. It is advisable to always use explicit padding (instead of relying on the current compiler, which may be replaced at some later point in time).
Supplemental:
These two rules are in essence the reflection of a single rule - every variable must be allocated at a memory address that is divisible by its size (1, 2, 4 or 8).
In most computer programs, the alignment problem emerges only when using structures.
But this is only because structure instances can more easily "fall into unaligned locations in memory", without generating any compilation warnings.
If we "try hard enough", then we can reproduce the same problem with simple variables. For example, in the code below, 3 out of 4 assignments will cause an unaligned memory access violation:
char arr[16];
int p0 = *(int*)(arr+0);
int p1 = *(int*)(arr+1);
int p2 = *(int*)(arr+2);
int p3 = *(int*)(arr+3);