Skip to content

`@alignOf`

@alignOf asks the Zig compiler for the required memory alignment of a type.

@alignOf

@alignOf asks the Zig compiler for the required memory alignment of a type.

Example:

const a = @alignOf(u32);

This means: “What alignment does u32 need?”

On many targets, u32 has alignment 4. That means a u32 value should usually start at a memory address divisible by 4.

What Alignment Means

Memory is a long sequence of bytes.

Each byte has an address:

address:  0  1  2  3  4  5  6  7
memory:   .  .  .  .  .  .  .  .

Some types are easiest for the CPU to read when they start at certain address boundaries.

For example, a u32 is 4 bytes. Many machines prefer a u32 to start at an address divisible by 4:

good u32 address: 0, 4, 8, 12, 16, ...

That is alignment.

If a type has alignment 4, then values of that type should start at addresses that are multiples of 4.

A Simple Program

const std = @import("std");

pub fn main() void {
    std.debug.print("u8 alignment:  {}\n", .{@alignOf(u8)});
    std.debug.print("u16 alignment: {}\n", .{@alignOf(u16)});
    std.debug.print("u32 alignment: {}\n", .{@alignOf(u32)});
    std.debug.print("u64 alignment: {}\n", .{@alignOf(u64)});
}

Possible output on a common 64-bit target:

u8 alignment:  1
u16 alignment: 2
u32 alignment: 4
u64 alignment: 8

These values are target-dependent. Zig can compile for many CPUs and operating systems, so exact alignment can vary.

Size and Alignment Are Different

@sizeOf and @alignOf answer different questions.

@sizeOf(u32)   // how many bytes it occupies
@alignOf(u32)  // where it should start in memory

For u32, both are often 4.

But that is not always true for every type.

A type’s size tells you how much memory it uses.

A type’s alignment tells you what address boundary it needs.

Why Alignment Exists

Alignment exists because CPUs do not always read memory one byte at a time.

A CPU often reads memory in chunks. If a value starts at a convenient address, the CPU can read it efficiently.

If a value starts at an inconvenient address, one of three things may happen:

The CPU reads it more slowly.

The CPU requires multiple memory operations.

The CPU rejects the access entirely on some architectures.

Zig cares about alignment because Zig cares about real machine behavior.

Struct Alignment

Structs also have alignment.

The alignment of a struct usually depends on the strongest alignment required by its fields.

Example:

const Point = struct {
    x: i32,
    y: i32,
};

Each i32 usually has alignment 4, so Point usually has alignment 4.

const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    std.debug.print("Point size: {}\n", .{@sizeOf(Point)});
    std.debug.print("Point alignment: {}\n", .{@alignOf(Point)});
}

Possible output:

Point size: 8
Point alignment: 4

The struct uses 8 bytes, but it needs to start on a 4-byte boundary.

Padding Comes from Alignment

In the previous section, we saw that structs may contain padding.

Alignment is the reason.

Example:

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

The field a uses 1 byte.

The field b usually wants alignment 4.

So the compiler may place padding between them:

byte 0: a
byte 1: padding
byte 2: padding
byte 3: padding
byte 4: b
byte 5: b
byte 6: b
byte 7: b

This gives the struct a size of 8 bytes on many targets.

You can inspect both size and alignment:

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("alignment: {}\n", .{@alignOf(Example)});
}

Possible output:

size: 8
alignment: 4

Field Order Can Affect Padding

Consider this struct:

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

Now compare it with this one:

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

They store the same data, but their memory layout may differ.

A may need padding before b and also after c.

B puts the largest field first, so it may need less padding.

const std = @import("std");

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

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

pub fn main() void {
    std.debug.print("A size: {}, alignment: {}\n", .{
        @sizeOf(A),
        @alignOf(A),
    });

    std.debug.print("B size: {}, alignment: {}\n", .{
        @sizeOf(B),
        @alignOf(B),
    });
}

Possible output:

A size: 24, alignment: 8
B size: 16, alignment: 8

The alignment is the same, but the size is different because the padding is different.

Alignment of Pointers

A pointer type also carries alignment information.

Example:

const p: *u32 = undefined;

A *u32 pointer means: this pointer points to a u32, and the address should satisfy the alignment required by u32.

That is different from a pointer to bytes:

const p: *u8 = undefined;

A u8 has alignment 1, so a *u8 can point to any byte address.

This matters when converting pointers. Zig will not let you casually treat any byte pointer as a pointer to a more strictly aligned type.

Alignment and @alignCast

Sometimes you know something about a pointer that Zig cannot prove.

For example, you may receive a byte pointer, but you know it points to memory aligned for u32.

Zig provides @alignCast for this situation.

A simplified example:

const bytes: [*]u8 = undefined;
const aligned: [*]align(4) u8 = @alignCast(bytes);

This tells Zig: treat this pointer as having 4-byte alignment.

Use this carefully. If the pointer is not actually aligned, the program can fail safety checks or behave incorrectly in unsafe contexts.

For beginners, the main idea is enough: @alignCast changes what Zig knows about pointer alignment. It does not magically move the memory.

Alignment and C Interop

Alignment matters a lot when Zig talks to C.

C structs have layout and alignment rules. When Zig calls C code or shares data with C, both languages must agree on how data is arranged in memory.

Example:

const CPoint = extern struct {
    x: c_int,
    y: c_int,
};

The extern struct tells Zig to use C ABI layout rules.

In this kind of code, @alignOf helps you inspect assumptions:

comptime {
    if (@alignOf(CPoint) != @alignOf(c_int)) {
        @compileError("unexpected CPoint alignment");
    }
}

This is useful when building bindings, binary protocols, or system interfaces.

Alignment and Packed Structs

Zig also has packed structs.

A packed struct asks Zig to store fields with a compact bit-level layout.

Example:

const Flags = packed struct {
    read: bool,
    write: bool,
    execute: bool,
};

Packed structs are useful for hardware registers, binary formats, and bit fields.

But packed layout changes the usual alignment and padding behavior.

Do not use packed structs just to “save memory” in ordinary application code. Use them when you need an exact representation.

Alignment Is Known at Compile Time

Like @sizeOf, @alignOf is evaluated at compile time.

You can store the result in a constant:

const alignment = @alignOf(u64);

You can use it in compile-time checks:

comptime {
    if (@alignOf(u64) < 8) {
        @compileError("expected u64 to have at least 8-byte alignment");
    }
}

This is useful when your code depends on target-specific assumptions.

When Beginners Should Care About Alignment

At the beginning, you do not need to optimize every struct layout.

But you should understand alignment because it explains many Zig behaviors:

Why structs have padding.

Why field order can affect memory use.

Why pointer casts are checked carefully.

Why C interop requires exact layout rules.

Why some low-level memory code needs extra care.

Most normal Zig code does not call @alignOf often. But when you work with memory layout, binary formats, allocators, C libraries, SIMD, or hardware, alignment becomes important.

Common Mistake: Thinking Alignment Is Size

This is wrong:

@alignOf(T) == @sizeOf(T)

It may be true for some primitive types, but it is not a rule.

Example:

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

On many targets:

@sizeOf(S)  = 8
@alignOf(S) = 4

The size is the total number of bytes in the struct.

The alignment is the address boundary required for the struct.

They answer different questions.

Common Mistake: Assuming All Targets Are the Same

Zig is a cross-compilation language.

The same source code can target different CPUs and operating systems.

So this kind of assumption can be fragile:

comptime {
    if (@alignOf(usize) != 8) {
        @compileError("only supports this one layout");
    }
}

Sometimes that is exactly what you want. But write such checks only when your program truly requires that target layout.

For portable code, prefer designing around Zig’s type system instead of hard-coding machine assumptions.

Key Idea

@alignOf(T) returns the memory alignment required by type T.

Alignment tells you where a value should start in memory.

It explains padding, struct layout, pointer rules, and many low-level details.

When you use @alignOf, you are asking the compiler about the physical layout requirements of a type.