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?