Introduction

In C, all the variables tend to have a particular liking about some specific memory addresses. These memory addresses are called their natural alignment or natural boundaries. The tendency of aligning of data types to their unique natural alignments is called Memory Alignment in C.

Example

Let us understand the working of memory with an example of structs.

Note: The working of memory stays the same without structs. An example of struct is taken just for simplicity of understanding.

struct Example {
    char a;    // 1 byte
    int b;     // 4 bytes
};

How the struct is laid out in memory:

01234567
apaddingpaddingpaddingbbbb

Why Align Data?

You must be thinking, it would be a waste of space if we keep aligning all the data to their particular boundaries. At the end, we sure waste the extra space by padding in between two data types as we saw in structs. But at the cost of space we are making memory access easier.
At the hardware level, memory is accessed in chunks called words, typically 2, 4, 8, or 16 bytes wide depending on the architecture. Say you have a 32-bit (4-byte word) CPU:

  • The CPU has a data bus that’s 4 bytes wide.
  • So each read/write from/to RAM ideally happens in 4-byte units aligned on 4-byte boundaries.

Here is an example of what would happen if the data is not aligned

struct Example {
    char a;    // 1 byte
    int b;     // 4 bytes
}__attribute((packed)); // Removes all padding between types

How the struct is laid out in memory:

01234567
abbbb---

The char and integer now cover 5 bytes of space in memory. But, we know that the process can access only in chunks. A 32-bit (4 byte) processor would need 2 cycles to access a single integer variable b.

  • First cycle to read bytes 1-3
  • Second cycle to read byte 4

After this, it needs to combine the seperate results internally to get the final integer. This breaks the fast, single-cycle access assumption.

Natural Alignments of Data Types

Alignment requirements depend on the data type and the architecture (e.g., 32-bit, 64-bit). On most platforms, the alignment of a data type is equal to its size, though not always.

Rule of Thumb

A data type’s natural boundary is any address which is divisible by n, where n is the size of the data type.

Here’s a general table for common types:

Data TypeSize (bytes)Alignment (bytes)Notes
char11No alignment needed
short22Aligned on 2-byte boundary
int44Aligned on 4-byte boundary
float44Same as int
long4 or 84 or 8Depends on architecture
long long88For 64-bit integers
double88On most systems
long double8, 12, or 168, 16Varies by compiler/ABI
void*4 (x86), 8 (x64)4 or 8Pointer size matches architecture
structDependsLargest member alignmentMight have padding

For more data types, refer Data Types