C Interoperability

34.1 Importing C Headers

@cImport runs translate-c on the headers and exposes functions, types, macros (simple ones), and constants.

The Zig team plans to move C translation into the build system (an addTranslateC step). Prefer the build-system approach for new projects:

const c = @cImport({
    @cDefine("_GNU_SOURCE", {});
    @cInclude("stdio.h");
    @cInclude("math.h");
    @cInclude("sqlite3.h");
});

pub fn main() void {
    _ = c.printf("Hello from C! %d\n", @as(c_int, 42));
    std.debug.print("{d}\n", .{c.sqrt(16.0)});
}
zig run main.zig -lc -lsqlite3            # link libc and the library
const translate_c = b.addTranslateC(.{
    .root_source_file = b.path("src/c.h"),
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("c", translate_c.createModule());

34.2 C Types in Zig

int, unsigned, long, char c_int, c_uint, c_long, c_char char* (string) [*:0]u8 / [*:0]const u8 T* (unknown)

[*c]T (translated), refine to *T, ?*T, [*]T extern struct ?*const fn (...) callconv(.c) T c_uint constants / enum(c_int)

34.3 Calling Conventions, Callbacks and Strings

fn compare(a: ?*const anyopaque, b: ?*const anyopaque) callconv(.c) c_int {
    const x: *const c_int = @ptrCast(@alignCast(a.?));
    const y: *const c_int = @ptrCast(@alignCast(b.?));
    return x.* - y.*;
}
var arr = [_]c_int{ 5, 2, 9 };
c.qsort(&arr, arr.len, @sizeOf(c_int), compare);

const zig_str: []const u8 = "hello";
const c_str = try allocator.dupeZ(u8, zig_str);   // null-terminated copy for C
defer allocator.free(c_str);
_ = c.puts(c_str.ptr);
const back: []const u8 = std.mem.span(c.getenv("HOME") orelse return);
_ = back;

34.4 Exporting Zig to C

Hand-write a header or use the build system to install one.

// mathlib.zig
export fn add(a: c_int, b: c_int) c_int {
    return a + b;
}
export fn process(buf: [*]u8, len: usize) void {
    const slice = buf[0..len];
    for (slice) |*b| b.* = std.ascii.toUpper(b.*);
}
zig build-lib mathlib.zig -dynamic         # libmathlib.so / .dll / .dylib
zig build-lib mathlib.zig                  # static library
zig build-lib mathlib.zig -femit-h         # header generation (availability varies by version)

34.5 translate-c

Complex macros (function-like with statements) may fail to translate. Write Zig equivalents manually.

zig translate-c -lc header.h > header.zig   # inspect or vendor generated bindings

34.6 Incrementally Porting C Projects

Replace the C build with build.zig (compile the existing C files with Zig).

Write new modules in Zig, exporting/importing the C ABI.

Port C files one at a time, keeping tests green.

Examples: Ghostty, Bun, and many libraries packaged as "zig build" wrappers (allyourcodebase on GitHub).