C notes

Tags :: Language

Memory alignment

Links

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;

References