# Pointer Arithmetic

### Pointer Arithmetic

Pointer arithmetic means moving a pointer forward or backward through memory.

In Zig, pointer arithmetic is not available on every pointer type. A single item pointer points to one value, so it does not support normal indexing or arithmetic. A many item pointer points into a sequence, so it can be moved.

```zig
*T    // single item pointer
[*]T  // many item pointer
```

A `*T` means “pointer to one `T`.”

A `[*]T` means “pointer to many `T` values starting here.”

Pointer arithmetic belongs to the second case.

#### Moving Through an Array

Start with an array:

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };

    const p: [*]u8 = &data;

    std.debug.print("{}\n", .{p[0]});
    std.debug.print("{}\n", .{p[1]});
}
```

Output:

```text
10
20
```

The pointer `p` points to the first item of `data`.

You can access later items with indexes:

```zig
p[0] // first item
p[1] // second item
p[2] // third item
```

This looks like array indexing, but the pointer itself does not know the array length.

That is the dangerous part.

#### Adding to a Pointer

You can add an integer to a many item pointer:

```zig
const q = p + 2;
```

If `p` points to `data[0]`, then `q` points to `data[2]`.

Example:

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };

    const p: [*]u8 = &data;
    const q = p + 2;

    std.debug.print("{}\n", .{q[0]});
}
```

Output:

```text
30
```

Memory can be pictured like this:

```text
data:   10   20   30   40
index:   0    1    2    3

p points to data[0]
q points to data[2]
```

So `q[0]` reads the same value as `data[2]`.

#### Pointer Arithmetic Moves by Items

Pointer arithmetic moves by items, not by raw bytes.

For a `[*]u8`, moving by 1 moves by 1 byte, because `u8` is 1 byte.

For a `[*]u32`, moving by 1 moves by 1 `u32`, which is usually 4 bytes.

Example:

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

pub fn main() void {
    var numbers = [_]u32{ 100, 200, 300, 400 };

    const p: [*]u32 = &numbers;
    const q = p + 1;

    std.debug.print("{}\n", .{q[0]});
}
```

Output:

```text
200
```

`q` points to the second `u32`, not to the second byte.

This is the right behavior. Pointer arithmetic follows the type.

#### Subtracting from a Pointer

You can also subtract from a many item pointer:

```zig
const q = p + 3;
const r = q - 2;
```

If `p` points to `data[0]`, then `q` points to `data[3]`, and `r` points to `data[1]`.

Example:

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

pub fn main() void {
    var data = [_]i32{ 10, 20, 30, 40 };

    const p: [*]i32 = &data;
    const q = p + 3;
    const r = q - 2;

    std.debug.print("{}\n", .{r[0]});
}
```

Output:

```text
20
```

The pointer moves through the same sequence.

The rule is simple:

`p + n` moves forward by `n` items.

`p - n` moves backward by `n` items.

#### Indexing Is Related to Pointer Arithmetic

These two expressions refer to the same item:

```zig
p[2]
(p + 2)[0]
```

The first form is clearer.

The second form shows what is happening underneath: move the pointer forward, then read the item there.

Example:

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

pub fn main() void {
    var data = [_]u8{ 5, 6, 7, 8 };

    const p: [*]u8 = &data;

    std.debug.print("{}\n", .{p[2]});
    std.debug.print("{}\n", .{(p + 2)[0]});
}
```

Output:

```text
7
7
```

Use indexing when you want to read an item at an offset.

Use explicit pointer movement only when that makes the low-level code clearer.

#### Pointer Arithmetic Has No Length Check

A many item pointer has no length.

This means Zig cannot know whether this is valid:

```zig
const x = p[100];
```

Maybe the memory has 101 items. Maybe it has only 4. The pointer type does not say.

This is why pointer arithmetic must always be controlled by a known bound.

Bad:

```zig
const value = p[1000];
```

Better:

```zig
var i: usize = 0;

while (i < len) : (i += 1) {
    const value = p[i];
    _ = value;
}
```

Best in ordinary Zig code:

```zig
for (slice) |value| {
    _ = value;
}
```

A slice carries a length. A many item pointer does not.

#### Converting to a Slice

When you have a pointer and a length, create a slice as soon as possible.

```zig
const slice = p[0..len];
```

Example:

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

fn printBytes(ptr: [*]const u8, len: usize) void {
    const bytes = ptr[0..len];

    for (bytes) |byte| {
        std.debug.print("{}\n", .{byte});
    }
}

pub fn main() void {
    const data = [_]u8{ 10, 20, 30 };

    printBytes(&data, data.len);
}
```

Output:

```text
10
20
30
```

The function receives low-level input:

```zig
ptr: [*]const u8, len: usize
```

Then it immediately turns that into a slice:

```zig
const bytes = ptr[0..len];
```

That is usually a good pattern. Do the unsafe-looking boundary work once, then use the safer slice form for the rest of the function.

#### Walking a Pointer Manually

Sometimes low-level code advances a pointer step by step.

```zig
var p = start;
p += 1;
p += 1;
```

Here is a complete example:

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

pub fn main() void {
    var data = [_]u8{ 10, 20, 30, 40 };

    var p: [*]u8 = &data;

    std.debug.print("{}\n", .{p[0]});

    p += 1;
    std.debug.print("{}\n", .{p[0]});

    p += 1;
    std.debug.print("{}\n", .{p[0]});
}
```

Output:

```text
10
20
30
```

Each `p += 1` moves the pointer to the next item.

This style appears in parsers, binary readers, C interop code, and performance-sensitive loops.

But it needs a clear stopping condition. Without a length or sentinel, the pointer does not know when to stop.

#### Using a Sentinel to Stop

Some sequences end with a sentinel value.

A common example is a C string, which ends with byte `0`.

Zig can represent this with a sentinel-terminated pointer:

```zig
[*:0]const u8
```

This means:

```text
many item pointer to const u8, ending with 0
```

Example:

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

fn printCString(s: [*:0]const u8) void {
    var p = s;

    while (p[0] != 0) : (p += 1) {
        std.debug.print("{c}", .{p[0]});
    }

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

pub fn main() void {
    printCString("zig");
}
```

Output:

```text
zig
```

The loop stops when it reaches the sentinel byte `0`.

Without the sentinel, this loop would not know where the string ends.

#### Pointer Difference

In low-level code, you may want to know how far two pointers are apart.

A simple way is to track the index yourself:

```zig
var i: usize = 0;

while (i < len) : (i += 1) {
    _ = ptr[i];
}
```

This is usually clearer than trying to compute distances from raw addresses.

When you need raw addresses, Zig provides builtins such as `@intFromPtr`, but beginners should avoid using addresses as integers unless there is a specific low-level reason.

Example:

```zig
const addr = @intFromPtr(p);
```

This gives you the pointer address as an integer. That is useful for debugging, allocators, kernels, embedded code, and special systems work.

For normal application code, prefer indexes and slices.

#### Pointer Arithmetic and Alignment

Pointer arithmetic respects the pointer’s element type, but you still must make sure the pointer itself is valid and properly aligned for that type.

A `[*]u32` should point to memory that is valid for `u32`.

This is different from a sequence of arbitrary bytes.

If you have raw bytes:

```zig
[]u8
```

do not casually treat them as:

```zig
[*]u32
```

The address may not be aligned for `u32`, and the bytes may not represent valid `u32` values in the way you expect.

This becomes important when reading binary formats, network packets, memory-mapped files, and C data structures.

For beginners, the practical rule is:

Do pointer arithmetic using the correct type for the memory you actually have.

#### Common Mistake: Moving Past the End

This is the classic pointer arithmetic bug:

```zig
var data = [_]u8{ 1, 2, 3 };
const p: [*]u8 = &data;

const bad = p + 10;
_ = bad[0];
```

The pointer moves far past the array.

The compiler cannot use the many item pointer alone to know this is wrong.

A safer version keeps the length close:

```zig
var data = [_]u8{ 1, 2, 3 };
const slice = data[0..];

for (slice) |value| {
    _ = value;
}
```

Use raw pointer arithmetic only when a slice cannot express what you need.

#### Common Mistake: Losing the Original Pointer

Sometimes code advances a pointer and later needs the original start address.

Bad:

```zig
var p = start;

// p moves many times
p += 1;
p += 1;
p += 1;

// original start is lost
```

Better:

```zig
const start_ptr = start;
var p = start;

// p can move
p += 1;

// start_ptr still points to the beginning
```

This matters when you need to free memory, compute how much was consumed, or return a slice from the original range.

As a rule, keep the original pointer or slice if ownership or bounds matter.

#### Slice Indexing Is Usually Better

Many pointer arithmetic examples can be written more safely with slices.

Pointer style:

```zig
fn sum(ptr: [*]const i32, len: usize) i32 {
    var total: i32 = 0;
    var i: usize = 0;

    while (i < len) : (i += 1) {
        total += ptr[i];
    }

    return total;
}
```

Slice style:

```zig
fn sum(values: []const i32) i32 {
    var total: i32 = 0;

    for (values) |value| {
        total += value;
    }

    return total;
}
```

The slice version is clearer. It says “I need a sequence of `i32` values.” The pointer version says “I need a raw starting address and a separate length.”

Use the slice version by default.

#### When Pointer Arithmetic Is Appropriate

Pointer arithmetic is useful when:

You are working with C APIs.

You are parsing raw memory.

You are writing allocators.

You are writing embedded or kernel code.

You are implementing low-level data structures.

You are walking a sentinel-terminated sequence.

You are building a safer abstraction on top of raw memory.

It is usually not needed for everyday Zig code.

#### The Main Idea

Pointer arithmetic lets you move a many item pointer through memory.

`p + n` moves forward by `n` items.

`p - n` moves backward by `n` items.

`p[i]` reads the item at offset `i`.

The pointer does not know the length of the sequence, so pointer arithmetic must always be guarded by a known length, a sentinel, or another clear boundary.

For normal code, use slices. For low-level code, use pointer arithmetic carefully and keep the bounds visible.

