🎓 A1Learner
🔍
0 of 55 lessons completed

Zig Pointers and Functions

A pointer parameter lets a function work on a caller's variable itself instead of a copy of its value. The function writes through the pointer, and the caller's variable changes.

addOne changes the caller's number

The function takes a *i32 and adds 1 through it. The variable in main holds the new value after the call.

const std = @import("std");

fn addOne(p: *i32) void {
    p.* += 1;
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var count: i32 = 10;
    addOne(&count);
    try out.print("{d}\n", .{count});
}

swap exchanges two caller variables

swap takes two pointers and trades the values they point at. A saved copy of the first value keeps it from being lost.

const std = @import("std");

fn swap(a: *i32, b: *i32) void {
    const saved = a.*;
    a.* = b.*;
    b.* = saved;
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var x: i32 = 3;
    var y: i32 = 7;
    swap(&x, &y);
    try out.print("x = {d}\ny = {d}\n", .{ x, y });
}

A pointer can sit beside plain parameters

addAmount takes a pointer and a plain i32 amount. Each call adds the amount to the same caller variable.

const std = @import("std");

fn addAmount(p: *i32, amount: i32) void {
    p.* += amount;
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var score: i32 = 10;
    addAmount(&score, 5);
    addAmount(&score, 3);
    try out.print("{d}\n", .{score});
}

Example

const std = @import("std");

fn addOne(p: *i32) void {
    p.* += 1;
}

fn swap(a: *i32, b: *i32) void {
    const saved = a.*;
    a.* = b.*;
    b.* = saved;
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var a: i32 = 4;
    var b: i32 = 9;
    try out.print("before: {d} {d}\n", .{ a, b });
    swap(&a, &b);
    addOne(&a);
    try out.print("after: {d} {d}\n", .{ a, b });
}

Key points

  • Pointer parameter: the function receives a *i32 that points at the caller's variable.
  • Write through it: p.* = value inside the function changes the caller's variable.
  • Address at the call: pass &x so the function gets a pointer to x.
  • Read through it: p.* reads the caller's current value inside the function.
  • No return needed: a change through the pointer is already visible after the call.

In this example, main starts with a holding 4 and b holding 9 and prints both. swap(&a, &b) trades the values through the two pointers, so a holds 9 and b holds 4. addOne(&a) then raises a to 10, and the last print shows 10 and 4.

Note

passing a plain i32 copies the value, so the function cannot change the original.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A function is written as fn reset(p: *i32) void and its body sets p.* = 0. What happens when main calls reset(&level) while level holds 5?