# Pointer Casts

### Pointer Casts

A pointer has a type.

```zig
var x: i32 = 10;
const p: *i32 = &x;
```

Here `p` points to an `i32`.

The type `*i32` says three things:

It points to memory.

That memory is treated as an `i32`.

The address must be valid for an `i32`, including alignment.

Most pointer conversions should be avoided. They make the program depend on memory layout. Sometimes they are needed: device registers, file formats, network packets, allocators, and C interfaces all require exact control over bytes.

Zig makes such conversions explicit.

```zig
const p2: *u32 = @ptrCast(p);
```

`@ptrCast` changes the pointer type. It does not move memory. It does not convert the value stored in memory. It only tells the compiler to view the same address through another pointer type.

This is a cast of the pointer, not a cast of the object.

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

pub fn main() void {
    var x: u32 = 0x11223344;

    const p: *u32 = &x;
    const b: *[4]u8 = @ptrCast(p);

    std.debug.print("{x} {x} {x} {x}\n", .{
        b.*[0],
        b.*[1],
        b.*[2],
        b.*[3],
    });
}
```

The bytes printed depend on the target's byte order. On a little-endian machine, the first byte is the least significant byte.

A pointer cast does not solve alignment.

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

const p: *u32 = @ptrCast(&bytes[1]);
```

The address of `bytes[1]` may not be aligned for `u32`. A `u32` usually requires stricter alignment than a `u8`.

Use `@alignCast` when the program has a reason to assert alignment.

```zig
const p: *u32 = @ptrCast(@alignCast(&bytes[1]));
```

This says: first assert that the address is correctly aligned, then treat it as a `*u32`.

If the assertion is false in a safe build mode, the program panics.

Pointer casts are common at C boundaries.

```zig
const c_ptr: *anyopaque = getPointerFromC();
const state: *State = @ptrCast(@alignCast(c_ptr));
```

`*anyopaque` means a pointer to memory of unknown type. It is similar in purpose to C's `void *`.

To use it, the program must know what the pointer really points to.

The cast is a promise.

It promises that the address points to a `State`.

It promises that the memory is aligned for `State`.

It promises that the object is alive for as long as the pointer is used.

If any of these promises is false, the program has undefined behavior.

Integer-to-pointer conversion is even sharper.

```zig
const p: *u32 = @ptrFromInt(0x1000);
```

This creates a pointer from an address.

The address must actually refer to valid memory for a `u32`. The memory must be aligned. The program must have permission to read or write it.

This is used in low-level code, such as memory-mapped I/O.

```zig
const reg: *volatile u32 = @ptrFromInt(0x4000_0000);
```

`volatile` tells the compiler that reads and writes must happen as written. This matters for hardware registers, where reading or writing memory can have effects outside the program.

Do not use integer-to-pointer conversion to invent ordinary program pointers. Use `&x`, allocation, or slices.

A many-item pointer can also be cast.

```zig
const bytes: [*]u8 = @ptrCast(ptr);
```

The type `[*]u8` is a pointer to many `u8` values. It has no length. A slice, `[]u8`, carries both pointer and length.

Prefer slices when a length is known.

```zig
const s: []u8 = buffer[0..];
```

Use many-item pointers when working with C APIs or low-level memory where the length is managed separately.

The safest form of reinterpretation is often to copy bytes instead of casting pointers.

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

pub fn main() void {
    const bytes = [_]u8{ 0x44, 0x33, 0x22, 0x11 };

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

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

This reads an integer from bytes with an explicit byte order. It does not depend on the address alignment of the byte array.

When reading file formats and network protocols, this is usually better than casting a pointer to a struct.

A pointer cast should be small and local. Put it close to the code that proves it is valid.

Poor style:

```zig
const p: *Header = @ptrCast(ptr);

// many lines later
useHeader(p);
```

Better style:

```zig
const p: *Header = @ptrCast(@alignCast(ptr));
useHeader(p);
```

The proof and the use are next to each other.

Pointer casts are part of Zig because systems programs need them. They are not a general programming tool.

Use them when the program is really about memory representation.

Exercise 19-9. Create a `u32`, cast its pointer to `*[4]u8`, and print the four bytes.

Exercise 19-10. Explain why the output of Exercise 19-9 depends on byte order.

Exercise 19-11. Replace a pointer cast used for reading bytes with `std.mem.readInt`.

Exercise 19-12. Write a small example that stores a typed pointer as `*anyopaque` and casts it back.

