ZigBasics

Zig Pointers and Slices: Memory Mastery

TT
TopicTrick Team
Zig Pointers and Slices: Memory Mastery

Zig Pointers and Slices: Memory Mastery

In C, "Pointers and Arrays" are often the same thing—a "Decay" behavior that has caused over 70% of the world's most critical security vulnerabilities. When a pointer has no idea how much data it is pointing to, "Buffer Overflows" become inevitable. Zig solves this by providing three distinct levels of memory access: Type-Safe Pointers, Many-Item Pointers, and the revolutionary Slices.

This 1,500+ word guide is the most important part of your Zig education. We will explore the anatomy of the "Fat Pointer," learn how Slices provide "Runtime Bounds Checking" without sacrificing performance, and understand how to manage memory addresses without the "Pointer Hell" that plagued systems programming for decades.


1. Single-Item Pointers (*T)

A single-item pointer points to exactly one instance of a type.

zig

The Safety Constraint: No Arithmetic

Unlike C or C++, you cannot perform calculations on a single-item pointer.

  • hp_ptr + 1 is a Compile Error.
  • Why? Because if you have a pointer to a User struct, there is no logical reason to move that pointer by 1 byte. By forbidding arithmetic, Zig ensures that a pointer to a variable stay a pointer to that variable.

2. The Physics of the Pointer: Memory Address Bit-Widths

To the hardware, a pointer is not an object; it is a 64-bit Integer (on modern systems) representing a physical coordinate in your RAM.

The Address Bus Mirror

  • The Concept: When you dereference a pointer (ptr.*), the CPU sends that 64-bit address across the Address Bus.
  • The Physics: If the address is invalid (not mapped to your process), the hardware generates a Segmentation Fault at the silicon level.
  • Zig's Safety: Zig's pointer types are strictly checked at compile-time to ensure you aren't accidentally treating an integer as an address unless you explicitly use @ptrFromInt. This prevents the "Memory Randomization" attacks common in C.

3. Many-Item Pointers ([*]T)

If you are writing a low-level driver, a custom allocator, or interacting with a legacy C library, you might need to treat memory as a raw stream. This is where the Many-Item Pointer comes in.

zig

The "Danger Zone"

Many-item pointers have No Bounds Checking. If the buffer is 5 bytes long and you ask for data[100], Zig will not stop you. This is the only part of Zig that behaves like C. Mastery Rule: Use many-item pointers only for the "Entry Point" of external data. Immediately convert them into Slices for safe consumption within your app.


3. Slices: The "Fat Pointer" Architecture ([]T)

A Slice is the "Gold Standard" for data handling in Zig. Internally, a slice is not just a pointer; it is a simple struct:

  1. ptr: A pointer to the start of the memory.
  2. len: A usize representing the length.
zig

Why Slices are Superior

  • Bounds Checking: If you try to access slice[10], Zig will detect the error at runtime (in Debug/ReleaseSafe) and stop the app safely.
  • Unified API: A function that accepts []const u8 can work on a literal string, an array on the stack, or memory allocated on the heap. This makes your code modular and reusable.

4. Sentinel Termination: The C-String Hybrid

Zig introduces a unique feature for interacting with operating systems: Sentinel-Terminated Pointers/Slices.

  • [*:0]u8: A many-item pointer that ends with a 0 (Null-terminated).
  • [:0]u8: A slice that ends with a 0.

This allows Zig to have "C-style" strings that still benefit from Zig's length-aware safety. You can convert a standard slice to a sentinel-terminated slice using @ptrCast or by slicing with an explicit sentinel: arr[0..5 :0].


5. Fat Pointer Architecture: The Cost of Safety

A Slice ([]T) is known as a Fat Pointer. This is an architectural choice that trades a tiny bit of memory for massive safety.

The CPU Register Cost

  • Standard Pointer: Takes 1 Register (8 bytes).
  • Slice: Takes 2 Registers (16 bytes: 8 for the pointer, 8 for the length).
  • The Tradeoff: When you pass a slice to a function, the CPU must move two registers. However, this allows the function to perform Bounds Checking with a single CMP (Compare) instruction.
  • The Mirror: The cost of a few extra clock cycles to check the length is infinitely cheaper than the multi-million dollar cost of a buffer-overflow breach.

6. Pointer Alignment: The Invisible Wall

Memory addresses are not just numbers; they have Alignment. An f64 (8 bytes) usually needs to be stored at an address divisible by 8.

  • If you try to cast a u8 pointer to a u64 pointer, Zig will verify the alignment.
  • If the alignment is wrong, the compiler will catch it. This prevents "Misaligned Access" crashes that are notoriously difficult to debug on certain CPU architectures (like ARM).

6. Volatile Pointers: Talking to Hardware

When writing embedded code (Module 26), you often point to a "Memory-Mapped I/O" (MMIO) register. The CPU might optimize away reads/writes to these addresses because it doesn't see why the value would change.

  • Use volatile pointers to tell the compiler: "Do not optimize this. This memory can change at any time because it belongs to the hardware."
zig

Summary: The Memory Checklist

  1. Single-Item (*T): Use for passing references to structs and modifying state.
  2. Slices ([]T): Use for almost all arrays, strings, and buffers.
  3. Many-Item ([*]T): Use only for C-interop or manual memory math.
  4. Bounds Checking: Always use slices so the compiler can protect your memory boundaries.
  5. Alignment: Respect @alignOf to ensure your pointers work on all CPU types.

Pointers are the "Wiring" of your application. By mastering the safety of single-item pointers and the visibility of Slices, you gain the ability to build massive systems that use $10x$ less RAM than Java while being just as safe. You graduate from "Managing variables" to "Architecting the Memory Map."


Phase 4: Memory Grid Checklist

  • Audit your function signatures: Replace Many-Item Pointers ([*]T) with Slices ([]T) wherever length is known.
  • Implement Alignment Verification: Use @alignOf(T) to ensure your pointers align with hardware boundaries.
  • Use Sentinel Slices ([:0]T) when bridging data between Zig logic and C-based system APIs.
  • Leverage Volatile Pointers for any memory-mapped I/O or shared-memory concurrency patterns.
  • Test Bounds Checking: Trigger a voluntary runtime panic by accessing a slice out-of-bounds in a Debug build to verify your safety net.

Read next: Control Flow: If, While, and Switch Physics →


Part of the Zig Mastery Course — engineering the memory.