# Appendix G. C Interop Reference

## Appendix G. C Interop Reference

Zig is designed to work closely with C. You can call C from Zig, call Zig from C, compile C code with Zig, and link Zig programs against existing C libraries.

This appendix gives the practical reference you need first.

### G.1 Importing a C Header

Use `@cImport` with `@cInclude`.

```zig
const c = @cImport({
    @cInclude("stdio.h");
});
```

Then call C functions through `c`.

```zig
pub fn main() void {
    _ = c.printf("Hello from C\n");
}
```

The `_ =` discards the return value intentionally.

### G.2 Calling C Functions

C functions keep their C names.

```zig
const c = @cImport({
    @cInclude("stdlib.h");
});

pub fn main() void {
    const n = c.abs(-10);
    _ = n;
}
```

Zig checks the imported C declarations and gives you typed access to them.

### G.3 C Strings

C strings are null-terminated byte pointers.

In Zig, a C string type often appears as:

```zig
[*c]const u8
```

A Zig string literal can usually be passed to C when it has a sentinel:

```zig
_ = c.printf("hello\n");
```

For dynamic strings, make sure the buffer is null-terminated before passing it to C.

### G.4 Include Paths

If Zig cannot find a header, add an include path.

```bash
zig build-exe main.zig -I/usr/include
```

In `build.zig`, include paths are usually added to the executable or module.

```zig
exe.addIncludePath(.{ .cwd_relative = "include" });
```

### G.5 Linking C Libraries

If a C function comes from a library, you must link that library.

Example with math library on Unix-like systems:

```bash
zig build-exe main.zig -lc -lm
```

In `build.zig`:

```zig
exe.linkLibC();
exe.linkSystemLibrary("m");
```

`linkLibC()` links the C standard library.

### G.6 Compiling C with Zig

Zig can act as a C compiler.

```bash
zig cc hello.c -o hello
```

It can also cross-compile C programs.

```bash
zig cc hello.c -target x86_64-windows-gnu -o hello.exe
```

This is useful even in projects that contain no Zig code.

### G.7 Mixing Zig and C Source Files

A Zig project can compile C files as part of the build.

```zig
exe.addCSourceFile(.{
    .file = b.path("src/helper.c"),
    .flags = &.{ "-std=c11" },
});
exe.linkLibC();
```

Then import the C header from Zig:

```zig
const c = @cImport({
    @cInclude("helper.h");
});
```

### G.8 Exporting Zig Functions to C

Use `export` to make a Zig function visible to C.

```zig
export fn add(a: c_int, b: c_int) c_int {
    return a + b;
}
```

Use C-compatible types such as:

```zig
c_int
c_uint
c_char
c_long
```

These match the target platform’s C ABI.

### G.9 Calling Zig from C

Suppose Zig exports this:

```zig
export fn add(a: c_int, b: c_int) c_int {
    return a + b;
}
```

A C header might declare:

```c
int add(int a, int b);
```

Then C code can call:

```c
int x = add(2, 3);
```

The important rule: exported Zig functions must use a C-compatible ABI and C-compatible types.

### G.10 Structs from C

C structs imported into Zig can be accessed through `c`.

C:

```c
struct Point {
    int x;
    int y;
};
```

Zig:

```zig
const p = c.struct_Point{
    .x = 10,
    .y = 20,
};
```

C names may be translated slightly so they fit Zig’s namespace rules.

### G.11 Passing Struct Pointers

Many C APIs take pointers to structs.

```zig
var p = c.struct_Point{
    .x = 1,
    .y = 2,
};

c.move_point(&p);
```

The `&p` passes the address of `p`.

### G.12 C Pointers

C pointers are less strict than normal Zig pointers.

You may see:

```zig
[*c]T
```

This means a C pointer. It may behave like a nullable pointer or pointer-like value depending on context.

Prefer converting C data into safer Zig types when possible.

For example, if C gives you a pointer and a length, convert it to a slice:

```zig
const slice = ptr[0..len];
```

Then use the slice in Zig code.

### G.13 Null from C

C often uses `NULL`.

In Zig, C pointers may need null checks.

```zig
const ptr = c.get_data();

if (ptr == null) {
    return error.NoData;
}
```

Do not dereference a C pointer before checking whether it is valid.

### G.14 C Memory Allocation

C APIs may allocate memory using `malloc`.

```zig
const ptr = c.malloc(100);
defer c.free(ptr);
```

Memory allocated by C should usually be freed by C.

Memory allocated by Zig should usually be freed by the Zig allocator that created it.

Do not mix allocation systems unless the API explicitly allows it.

### G.15 Zig Allocators and C APIs

Some C APIs let you provide custom allocation callbacks.

This is how you can connect Zig’s allocator model to C libraries.

The pattern is usually:

1. Store a pointer to a Zig allocator or context.
2. Provide C-compatible callback functions.
3. Cast the user data pointer back to your Zig type.
4. Allocate or free using the Zig allocator.

This is advanced. Start with normal C allocation rules first.

### G.16 C Macros

Zig can import many C macros, but not all macros translate cleanly.

Simple constant macros often work:

```c
#define MAX_SIZE 1024
```

Function-like macros may not work the way you expect:

```c
#define SQUARE(x) ((x) * (x))
```

For complex macros, write a small C wrapper function.

C wrapper:

```c
int square_int(int x) {
    return SQUARE(x);
}
```

Then call `square_int` from Zig.

### G.17 C Enums

C enums usually import as integer-like values.

C:

```c
enum Color {
    RED,
    GREEN,
    BLUE,
};
```

Zig:

```zig
const color = c.RED;
```

C enums are less strict than Zig enums. Treat them carefully when crossing the boundary.

### G.18 C Header Translation

`@cImport` runs C translation.

That means Zig reads the header and creates Zig declarations from it.

This depends on:

| Input | Why it matters |
|---|---|
| Header files | Define functions, structs, macros |
| Include paths | Let Zig find headers |
| Target | Affects type sizes and ABI |
| C compiler flags | Affect conditional declarations |
| Linked libraries | Provide implementation |

If translation fails, check headers and include flags first.

### G.19 Common C Interop Errors

| Error | Usual cause |
|---|---|
| Header not found | Missing `-I` include path |
| Undefined symbol | Library not linked |
| Type mismatch | Wrong Zig type or pointer type |
| Invalid null pointer | Missing null check |
| Macro missing | Macro cannot be translated |
| ABI mismatch | Wrong calling convention or target |

### G.20 `extern` Declarations

You can declare C functions manually with `extern`.

```zig
extern fn puts(s: [*:0]const u8) c_int;
```

Then call it:

```zig
pub fn main() void {
    _ = puts("hello");
}
```

Use `@cImport` when possible. Use `extern` when you need a small manual declaration.

### G.21 Calling Convention

C ABI functions use the C calling convention.

```zig
fn callback(x: c_int) callconv(.c) void {
    _ = x;
}
```

Use this when passing a Zig function pointer to C.

### G.22 Callbacks from C into Zig

C libraries often accept callbacks.

Zig callback:

```zig
fn onEvent(value: c_int) callconv(.c) void {
    _ = value;
}
```

Pass it to C:

```zig
c.set_callback(onEvent);
```

The callback must use the calling convention expected by the C library.

### G.23 Opaque Types

Some C libraries hide struct definitions.

C:

```c
typedef struct Database Database;
```

Zig treats this as an opaque type. You can pass pointers around, but you cannot access fields.

```zig
const db = c.db_open("file.db");
defer c.db_close(db);
```

This is common in C APIs. It is a good design for hiding implementation details.

### G.24 Sentinels and C Strings

Zig has sentinel-terminated pointer and slice types.

```zig
[:0]const u8
[*:0]const u8
```

The `0` means the data ends with a zero byte.

This matches C strings.

Example:

```zig
const name: [:0]const u8 = "zig";
```

Use sentinel types when an API needs a null-terminated string.

### G.25 Practical Rule

At the Zig-C boundary, be conservative.

Use C-compatible types.

Check nulls.

Match allocation and free functions.

Use the correct calling convention.

Wrap unsafe C APIs behind small Zig functions.

A good Zig wrapper turns a loose C API into a safer Zig API.

