# C Pointers

> [*c]T: the escape hatch, and why to leave it at the door.

```zig
const std = @import("std");
const expect = std.testing.expect;

test "a C pointer is the permissive one" {
    var value: i32 = 42;
    // [*c]T can be null, can be indexed, and coerces freely. It gives up
    // every guarantee Zig's other pointer types provide.
    const c_ptr: [*c]i32 = &value;
    try expect(c_ptr.* == 42);
    try expect(c_ptr[0] == 42);
}

test "it coerces to and from the safe types" {
    var array = [_]i32{ 1, 2, 3 };
    const many: [*]i32 = &array;
    const c_ptr: [*c]i32 = many;
    const back: [*]i32 = c_ptr;
    try expect(back[2] == 3);
}

test "null is representable" {
    const c_ptr: [*c]i32 = null;
    try expect(c_ptr == null);
}

test "convert at the boundary and use real types inside" {
    var array = [_]i32{ 10, 20, 30 };
    const c_ptr: [*c]i32 = &array;

    // The length is not carried by the pointer; supply it once, then work
    // with a bounds-checked slice.
    const slice: []i32 = c_ptr[0..3];
    try expect(slice.len == 3);
    try expect(slice[1] == 20);
}
```

*Runnable: compiled to WebAssembly and executed by CI against Zig master. (`05-working-with-c.c-pointers`)*

`[*c]T` is Zig's model of C's `T*`. It exists almost entirely so that
`@cImport` can translate headers automatically.

## It gives up everything

| Property | `*T` | `[]T` | `[*c]T` |
| --- | --- | --- | --- |
| can be null | no | no | **yes** |
| carries a length | n/a | yes | no |
| bounds checked | n/a | yes | no |
| indexable | no | yes | yes |
| coerces freely | no | no | **yes** |

That last row is the dangerous one. A `[*c]T` converts to and from the safe
pointer types without complaint, which is convenient at a C boundary and
corrosive anywhere else: it silently launders away the guarantees the rest of
the language is built on.

## Convert at the boundary

The moment you know the length, produce a slice and never look back:

```zig
const slice: []i32 = c_ptr[0..count];
```

Now you have bounds checks, `for` iteration, and `.len`. If the pointer might
be null, check once and turn it into an optional rather than propagating the
uncertainty.

## Rule of thumb

If you did not get it from `@cImport` or an `extern` declaration, you should
not be writing `[*c]`. Use `*T` for one item, `[*]T` for many, `[]T` when you
know the count, and `?` when it may be absent.
