# Alignment

### Alignment

Alignment is a rule about where a value may be placed in memory.

Every value has a size. Many values also have an alignment requirement. The size tells you how many bytes the value occupies. The alignment tells you what kind of address is acceptable for the value.

For example, a `u8` can usually live at any byte address. A `u32` often wants to live at an address divisible by 4. A `u64` often wants to live at an address divisible by 8.

This is not mainly a Zig rule. It comes from the machine. CPUs are usually faster and sometimes stricter when values are placed at properly aligned addresses.

#### Size vs Alignment

Size and alignment are related, but they are not the same thing.

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("u8:  size={}, align={}\n", .{ @sizeOf(u8), @alignOf(u8) });
    std.debug.print("u32: size={}, align={}\n", .{ @sizeOf(u32), @alignOf(u32) });
    std.debug.print("u64: size={}, align={}\n", .{ @sizeOf(u64), @alignOf(u64) });
}
```

Typical output:

```text
u8:  size=1, align=1
u32: size=4, align=4
u64: size=8, align=8
```

`@sizeOf(T)` tells you how many bytes a value of type `T` occupies.

`@alignOf(T)` tells you the default alignment requirement for type `T`.

An alignment of `4` means the address should be divisible by `4`.

An alignment of `8` means the address should be divisible by `8`.

#### Why Alignment Exists

Memory is addressed byte by byte, but CPUs usually load and store larger chunks.

If a `u32` is stored at an address divisible by 4, the CPU can often load it cleanly in one operation.

```text
address:  1000  1001  1002  1003
bytes:    [        one u32        ]
```

Address `1000` is divisible by 4, so this is aligned for a 4-byte value.

But this would be unaligned:

```text
address:  1001  1002  1003  1004
bytes:    [        one u32        ]
```

Address `1001` is not divisible by 4.

Some CPUs can handle this, but it may be slower. Some CPUs may trap. Zig makes alignment visible because systems code must care about these details.

#### Alignment in Pointers

Pointer types can carry alignment information.

A normal pointer to `u32` assumes the pointer is properly aligned for `u32`.

```zig
var x: u32 = 123;
const p: *u32 = &x;
```

Here, `p` points to a valid `u32`. Zig knows the address is aligned correctly.

This matters when dereferencing:

```zig
const value = p.*;
```

Dereferencing a pointer means reading or writing the value at that address. That operation is only valid if the pointer points to properly aligned memory for that type.

#### Seeing an Address

You can inspect a pointer address with `@intFromPtr`.

```zig
const std = @import("std");

pub fn main() void {
    var x: u32 = 123;
    const p = &x;

    std.debug.print("address = {x}\n", .{@intFromPtr(p)});
    std.debug.print("align   = {}\n", .{@alignOf(u32)});
}
```

The printed address will vary each time and on each machine.

The important idea is that a `*u32` should point to an address compatible with `@alignOf(u32)`.

#### Struct Padding

Alignment affects structs.

Consider this struct:

```zig
const Example = struct {
    a: u8,
    b: u32,
};
```

You may think the size is:

```text
1 byte for a
4 bytes for b
total: 5 bytes
```

But the actual size is often larger.

```zig
const std = @import("std");

const Example = struct {
    a: u8,
    b: u32,
};

pub fn main() void {
    std.debug.print("size  = {}\n", .{@sizeOf(Example)});
    std.debug.print("align = {}\n", .{@alignOf(Example)});
}
```

Typical output:

```text
size  = 8
align = 4
```

Why 8?

Because `b` wants to start at an address divisible by 4. After `a`, Zig may insert padding bytes before `b`.

Memory may look like this:

```text
a:       1 byte
padding: 3 bytes
b:       4 bytes
total:   8 bytes
```

Padding bytes exist only to satisfy alignment.

#### Field Order Can Change Size

Field order can affect struct size.

```zig
const A = struct {
    a: u8,
    b: u32,
    c: u8,
};

const B = struct {
    b: u32,
    a: u8,
    c: u8,
};
```

These structs contain similar data, but their layout may have different padding.

```zig
const std = @import("std");

const A = struct {
    a: u8,
    b: u32,
    c: u8,
};

const B = struct {
    b: u32,
    a: u8,
    c: u8,
};

pub fn main() void {
    std.debug.print("A size = {}, align = {}\n", .{ @sizeOf(A), @alignOf(A) });
    std.debug.print("B size = {}, align = {}\n", .{ @sizeOf(B), @alignOf(B) });
}
```

A common result is:

```text
A size = 12, align = 4
B size = 8, align = 4
```

The second layout wastes less space because the largest field comes first.

This matters in large arrays of structs. Saving 4 bytes per item is small for one value, but large for millions of values.

#### Alignment and Arrays

Arrays store items one after another.

```zig
const data: [4]u32 = .{ 1, 2, 3, 4 };
```

Each `u32` must be properly aligned. Since each item has size 4 and alignment 4, this is straightforward.

For structs, padding matters because every array element must also keep the next element aligned.

```zig
const items: [1000]Example = undefined;
```

If `Example` has size 8, then the array uses:

```text
1000 * 8 = 8000 bytes
```

Even if the visible fields only seem to need 5 bytes each.

The size of a type includes the padding needed for arrays to work correctly.

#### Explicit Alignment

Zig lets you request a specific alignment.

```zig
var buffer: [1024]u8 align(16) = undefined;
```

This asks Zig to place `buffer` at an address aligned to 16 bytes.

This is useful for SIMD, operating system APIs, hardware devices, memory allocators, and binary protocols that require particular alignment.

You can also see alignment in pointer types:

```zig
*align(16) u8
```

This means a pointer to `u8` whose address is aligned to 16.

Most beginner code does not need explicit alignment. But systems code often does.

#### Alignment and Byte Buffers

A common low-level mistake is treating arbitrary bytes as a larger type.

Suppose you have a byte buffer:

```zig
var bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
```

It may be tempting to read part of it as a `u32`.

But a `u32` pointer requires proper alignment. A random position inside a `[]u8` buffer may not be aligned for `u32`.

This is risky:

```zig
// Dangerous idea in low-level code:
// treat bytes[1] as the start of a u32
```

The address of `bytes[1]` is one byte after the start of the array. Even if `bytes` itself is aligned, `bytes[1]` may not be aligned for `u32`.

When reading binary data, prefer explicit byte parsing:

```zig
const std = @import("std");

pub fn main() void {
    const bytes = [_]u8{ 0x78, 0x56, 0x34, 0x12 };

    const value = std.mem.readInt(u32, bytes[0..4], .little);

    std.debug.print("{x}\n", .{value});
}
```

This reads bytes as a little-endian `u32` without pretending the byte buffer is already a properly aligned `u32`.

#### Alignment and C Interop

Alignment matters when working with C.

C structs have layout and alignment rules. Zig can match C layout with `extern struct`.

```zig
const CPoint = extern struct {
    x: i32,
    y: i32,
};
```

Use `extern struct` when the struct must match a C ABI.

Do not assume a normal Zig `struct` has the same layout as a C struct unless you explicitly use the right representation.

Alignment is part of ABI compatibility. If Zig and C disagree about field layout or alignment, data may be read incorrectly.

#### Packed Structs

Zig also has packed structs.

```zig
const Packed = packed struct {
    a: u8,
    b: u32,
};
```

A packed struct removes normal padding and stores fields more tightly.

That can be useful for bit-level formats or hardware registers.

But packed layout can also create unaligned fields. Accessing such fields may require extra care or generate different code.

Do not use `packed struct` just to save memory. Use it when the exact packed representation is required.

For ordinary data, normal structs are usually better.

#### Common Mistake: Assuming Struct Size

This assumption is often wrong:

```text
struct size = sum of field sizes
```

The real rule is:

```text
struct size = field sizes + padding needed for alignment
```

Always check with `@sizeOf` when layout matters.

```zig
std.debug.print("{}\n", .{@sizeOf(MyStruct)});
```

If the struct crosses an FFI boundary, file format, network format, or hardware boundary, treat layout as a design issue, not a guess.

#### Common Mistake: Ignoring Alignment After Pointer Casts

Pointer casts can create alignment problems.

If you cast a pointer from bytes to a larger type, you must know the address is correctly aligned.

Bad mental model:

```text
The bytes are there, so I can view them as any type.
```

Better mental model:

```text
The bytes must have the right size, the right meaning, and the right alignment.
```

All three matter.

#### Practical Rules

Use `@sizeOf(T)` to inspect size.

Use `@alignOf(T)` to inspect alignment.

Expect structs to contain padding.

Put larger fields earlier when memory layout matters.

Use `extern struct` for C layout.

Use `packed struct` only when packed representation is required.

Do not cast arbitrary byte buffers into typed pointers unless you know the alignment is valid.

For binary formats, parse bytes explicitly.

#### The Main Idea

Alignment is about valid addresses for values.

A type does not only say what a value means. It also affects where that value may safely live in memory.

Zig exposes alignment because Zig is used for systems programming, where layout matters. Most of the time, the compiler handles alignment for you. But when you work with pointers, binary data, packed structs, C interop, allocators, or hardware, alignment becomes part of correctness.

