# Passing Values

### Passing Values

When a function is called, values are passed from the caller to the function parameters.

Consider:

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

fn addOne(x: i32) i32 {
    return x + 1;
}

pub fn main() void {
    var n: i32 = 5;

    const m = addOne(n);

    std.debug.print("n = {d}, m = {d}\n", .{ n, m });
}
```

The output is:

```text
n = 5, m = 6
```

The value of `n` is copied into the parameter `x`.

Changing `x` does not change `n`.

This is called passing by value.

The parameter receives its own local copy.

Even when the original variable is mutable, the parameter itself is immutable:

```zig
fn f(x: i32) void {
    // x += 1;   illegal
}
```

To modify data belonging to the caller, a pointer must be passed.

Here is a function that changes a variable through a pointer:

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

fn increment(x: *i32) void {
    x.* += 1;
}

pub fn main() void {
    var n: i32 = 5;

    increment(&n);

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

The output is:

```text
6
```

The parameter type is:

```zig
*i32
```

which means “pointer to `i32`”.

The expression:

```zig
&n
```

produces the address of `n`.

Inside the function:

```zig
x.*
```

means “the value pointed to by `x`”.

The statement:

```zig
x.* += 1;
```

modifies the original variable.

Small values are usually passed directly:

```zig
fn square(x: i32) i32 {
    return x * x;
}
```

This is simple and efficient.

Larger objects are often passed by pointer:

```zig
const Buffer = struct {
    data: [1024]u8,
};

fn clear(buf: *Buffer) void {
    for (&buf.data) |*byte| {
        byte.* = 0;
    }
}
```

Passing a pointer avoids copying the entire object.

Arrays behave differently from slices.

This function takes a fixed-size array:

```zig
fn printArray(a: [4]i32) void {
    _ = a;
}
```

The size is part of the type.

Only arrays of exactly four elements may be passed.

Slices are more flexible:

```zig
fn printSlice(values: []const i32) void {
    _ = values;
}
```

A slice contains:

- a pointer
- a length

Slices allow functions to operate on arrays of many sizes.

Strings are usually passed as slices:

```zig
fn greet(name: []const u8) void {
    std.debug.print("hello, {s}\n", .{name});
}
```

The type:

```zig
[]const u8
```

means “read-only slice of bytes”.

A function may return a pointer, but care is required.

This is wrong:

```zig
fn bad() *i32 {
    var x: i32 = 10;
    return &x;
}
```

`x` stops existing when the function returns. The returned pointer becomes invalid.

This kind of mistake is common in systems programming. Zig tries to make ownership and lifetimes explicit.

Functions may take pointers that cannot modify data:

```zig
fn printNumber(x: *const i32) void {
    std.debug.print("{d}\n", .{x.*});
}
```

`*const i32` means the pointed-to value is read-only through this pointer.

The caller may still own mutable data, but the function promises not to modify it.

A good rule is:

- pass small values directly
- pass large objects by pointer
- use `const` whenever mutation is unnecessary

This keeps interfaces simple and predictable.

Exercise 4-9. Write a function `swap` that exchanges two integers using pointers.

Exercise 4-10. Write a function that sets every element of a slice to zero.

Exercise 4-11. Why is returning a pointer to a local variable unsafe?

Exercise 4-12. Change `increment` so it cannot modify the pointed-to value. What compiler error results?

