# Many Item Pointers

### Many Item Pointers

A many item pointer is a pointer that can move across several values of the same type.

Its type looks like this:

```zig
[*]T
```

Read it as:

```text
many item pointer to T
```

For example:

```zig
[*]u8
```

means:

```text
many item pointer to u8
```

A single item pointer, `*T`, points to one value.

A many item pointer, `[*]T`, points to the first value in a sequence.

The important difference is that a many item pointer supports indexing and pointer arithmetic, but it does not store a length.

#### Why Many Item Pointers Exist

Many item pointers are useful when working close to C, operating systems, buffers, and low-level memory.

C often represents arrays as pointers:

```c
unsigned char *buffer;
```

The pointer tells you where the buffer starts, but it does not tell you how long the buffer is.

Zig can represent that idea with:

```zig
[*]u8
```

This says:

```text
there may be many u8 values starting at this address
```

But Zig still does not know how many.

That is why many item pointers are lower-level than slices.

#### Many Item Pointer vs Slice

A slice is usually safer and more convenient:

```zig
[]T
```

A slice contains both a pointer and a length.

A many item pointer contains only a pointer.

| Type | Has pointer | Has length | Supports indexing | Typical use |
|---|---|---|---|---|
| `*T` | yes | no | no, only `p.*` | one value |
| `[*]T` | yes | no | yes | low-level sequences |
| `[]T` | yes | yes | yes, bounds checked | normal buffers |

For most Zig code, prefer slices.

Use many item pointers when you specifically need raw pointer-like behavior.

#### Creating a Many Item Pointer

You can get a many item pointer from an array by taking the address of its first element:

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

    const p: [*]u8 = &data;

    _ = p;
}
```

Here, `data` is an array of four `u8` values.

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

You can index it:

```zig
const first = p[0];
const second = p[1];
```

Full example:

```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 itself does not know that the array has 4 values. You must know that from somewhere else.

#### Indexing a Many Item Pointer

A many item pointer can be indexed like an array:

```zig
p[0]
p[1]
p[2]
```

But there is no length check based on the pointer itself.

This is dangerous:

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

Zig cannot know whether index `1000` is valid, because `p` does not carry a length.

If the original memory has only 4 items, then `p[1000]` is invalid.

This is the central risk of many item pointers.

A slice would be safer:

```zig
const s = data[0..];
const value = s[1000]; // bounds check in safe modes
```

With a slice, Zig knows the length.

With a many item pointer, Zig does not.

#### Pointer Arithmetic

Many item pointers support pointer arithmetic.

You can move the pointer forward:

```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
```

Why `30`?

Because `q` points to the third item.

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

p points here:
       10

q = p + 2 points here:
               30
```

Pointer arithmetic moves by elements, not by raw bytes.

If `p` is a `[*]u8`, then `p + 1` moves by 1 byte because `u8` is 1 byte.

If `p` is a `[*]u32`, then `p + 1` moves by 4 bytes because `u32` is 4 bytes.

The arithmetic is based on the pointed-to type.

#### Converting a Many Item Pointer to a Slice

A many item pointer has no length, but you can make a slice if you know the length.

```zig
const s = p[0..4];
```

This creates a slice of 4 items starting at `p`.

Example:

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

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

    const p: [*]u8 = &data;
    const s = p[0..4];

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

Output:

```text
{ 10, 20, 30, 40 }
```

This is common when calling low-level APIs.

You may receive:

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

Then you create:

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

Now you can use safer slice operations.

#### Passing Many Item Pointers with Lengths

Since a many item pointer does not know its length, functions usually pass a length separately.

```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;
}
```

Full example:

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

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;
}

pub fn main() void {
    const data = [_]i32{ 1, 2, 3, 4 };

    const result = sum(&data, data.len);

    std.debug.print("sum = {}\n", .{result});
}
```

Output:

```text
sum = 10
```

This works, but a slice version is cleaner:

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

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

    return total;
}
```

Prefer the slice version unless you have a reason to use raw pointer style.

#### Const Many Item Pointers

A many item pointer can point to mutable or immutable data.

Mutable:

```zig
[*]u8
```

This allows writing through the pointer.

Immutable:

```zig
[*]const u8
```

This allows reading, but not writing.

Example:

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

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

    while (i < len) : (i += 1) {
        std.debug.print("{}\n", .{ptr[i]});
    }
}

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

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

The function receives `[*]const u8`, so it promises not to modify the bytes.

A function that modifies the bytes would use:

```zig
fn clearBytes(ptr: [*]u8, len: usize) void {
    var i: usize = 0;

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

#### A Practical Example: Fill a Buffer

Here is a function that fills a buffer through a many item pointer:

```zig
fn fill(ptr: [*]u8, len: usize, value: u8) void {
    var i: usize = 0;

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

Use it like this:

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

fn fill(ptr: [*]u8, len: usize, value: u8) void {
    var i: usize = 0;

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

pub fn main() void {
    var data = [_]u8{ 1, 2, 3, 4 };

    fill(&data, data.len, 9);

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

Output:

```text
{ 9, 9, 9, 9 }
```

Again, this is valid, but the slice version is better for normal Zig:

```zig
fn fill(buffer: []u8, value: u8) void {
    for (buffer) |*byte| {
        byte.* = value;
    }
}
```

The slice carries its length. The function signature becomes simpler.

#### Sentinel-Terminated Many Item Pointers

Zig also has sentinel-terminated many item pointers.

They look like this:

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

This means:

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

This is common for C strings.

C strings are usually sequences of bytes ending with `0`.

For example:

```text
h e l l o 0
```

Zig can express that with a sentinel pointer.

Example:

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

pub fn main() void {
    const message: [*:0]const u8 = "hello";

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

A normal string literal in Zig has a zero byte at the end, so it can be used as a sentinel-terminated pointer.

This matters when calling C functions that expect strings ending in `0`.

#### Many Item Pointers and C Interop

Many item pointers often appear when calling C libraries.

Suppose a C function expects:

```c
void process(unsigned char *data, size_t len);
```

In Zig, this maps naturally to something like:

```zig
extern fn process(data: [*]u8, len: usize) void;
```

If the C function does not modify the data, it might be:

```zig
extern fn process(data: [*]const u8, len: usize) void;
```

For C strings:

```c
void puts(const char *s);
```

Zig may represent the string pointer as:

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

The sentinel tells Zig and the reader that the sequence ends with `0`.

#### When to Use Many Item Pointers

Use many item pointers when:

You are calling C code.

You are writing very low-level code.

You have a pointer and a separate length from an external API.

You need pointer arithmetic.

You are implementing abstractions that will later expose safer types.

Avoid many item pointers when:

A slice would work.

You want bounds checks.

You want the length to travel with the data.

You are writing normal application logic.

In most Zig programs, `[]T` appears more often than `[*]T`.

#### Common Mistake: Forgetting the Length

This function is unsafe as an API design:

```zig
fn printAll(ptr: [*]const u8) void {
    var i: usize = 0;

    while (true) : (i += 1) {
        std.debug.print("{}\n", .{ptr[i]});
    }
}
```

The function has no idea when to stop.

Unless the pointer is sentinel-terminated, a many item pointer needs a length.

Better:

```zig
fn printAll(ptr: [*]const u8, len: usize) void {
    var i: usize = 0;

    while (i < len) : (i += 1) {
        std.debug.print("{}\n", .{ptr[i]});
    }
}
```

Best for normal Zig:

```zig
fn printAll(bytes: []const u8) void {
    for (bytes) |byte| {
        std.debug.print("{}\n", .{byte});
    }
}
```

The slice version makes invalid use harder.

#### Common Mistake: Using Pointer Arithmetic Without a Clear Bound

This kind of code is risky:

```zig
var p: [*]u8 = some_pointer;

p += 1;
p += 1;
p += 1;
```

It may be valid, but only if you know the pointer still points inside valid memory.

Pointer arithmetic should always be tied to a known range.

For example:

```zig
var i: usize = 0;

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

This is clearer because `len` gives a boundary.

#### The Main Idea

A many item pointer points to the start of a sequence, but it does not know the sequence length.

That makes it powerful and dangerous.

Use it when you need low-level pointer behavior, especially for C interop or manual memory work.

For ordinary Zig code, prefer slices.

A slice gives you the same basic ability to access a sequence, but it also carries the length. That one extra piece of information makes the code much safer and easier to understand.

