C notes
Tags :: Language
Memory alignment
Links
- IBM - Data alignment: Straighten up and fly right
- Gallery of Processor Cache Effects
- x86 Protected Mode Basics
Modern computer architectures will always read memory at its “word size” (4 bytes on a 32 bit machine, 8 bytes on a 64 bit machine). If you have an unaligned memory access (on a processor that allows for that), the processor will have to read multiple “words”. This means that an unaligned memory access may be much slower than an aligned memory access.
Aligning a Memory Address
On virtually all architectures, the amount of bytes that something must be aligned by must be a power of two (1, 2, 4, 8, 16, etc). This means we should create procedure to assert that the alignment is a power of two:
bool is_power_of_two(uintptr_t x) {
return (x & (x-1)) == 0;
}
To align a memory address to the specified alignment is simple modulo arithmetic. You are looking to find how many bytes forward you need to go in order for the memory address is a multiple of the specified alignment.
uintptr_t align_forward(uintptr_t ptr, size_t align) {
uintptr_t p, a, modulo;
assert(is_power_of_two(align));
p = ptr;
a = (uintptr_t)align;
// Same as (p % a) but faster as 'a' is a power of two
modulo = p & (a-1);
if (modulo != 0) {
// If 'p' address is not aligned, push the address to the
// next value which is aligned
p += a - modulo;
}
return p;
}
Unions
The purpose of a union is this - a single variable that can legitimately hold any of one of several types. At different times, it can store objects of different types and sizes in a variable, with the compiler keeping track of size and alignment requirements. This means that with a union, data of different kinds can be manipulated in a single area of storage (the variable).
In the example below, any of those types may be assigned to the variable u
and used in expressions. The size of u
will be the size of the largest type within the union.
union u_tag {
int ival;
float fval;
char *sval;
} u;