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 libraryconst 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 bindings34.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).