Skip to content

`@cImport`

Writing every C declaration by hand is tedious. Zig can read C headers directly.

@cImport

Writing every C declaration by hand is tedious. Zig can read C headers directly.

The usual form is:

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

This creates a namespace named c. Declarations from the C header are available through that namespace.

A complete program:

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

pub fn main() void {
    _ = c.puts("hello from C");
}

Run it:

zig run main.zig -lc

The call:

c.puts("hello from C")

uses the C function declared in stdio.h.

The return value is assigned to _ because puts returns an int, and Zig does not allow an ignored value unless the programmer says so.

@cImport may include more than one header:

const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("stdlib.h");
    @cInclude("string.h");
});

It may also define macros before including a header:

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

The block inside @cImport is not ordinary runtime code. It is evaluated by the compiler while translating C declarations into Zig declarations.

Header search paths are passed to the compiler:

zig build-exe main.zig -Iinclude -lc

A header in include/foo.h can then be imported:

const c = @cImport({
    @cInclude("foo.h");
});

Use @cImport when the C API is large or when the declarations must track a real header file.

Use extern fn when the API is small and explicit declarations are clearer.