Skip to content

ABI and Layout

A C program and a Zig program can share data only when both sides agree on layout.

A C program and a Zig program can share data only when both sides agree on layout.

Layout means the exact order, size, alignment, and padding of values in memory. C code does not see Zig types. It sees bytes at addresses. If the bytes do not have the shape C expects, the program is wrong.

For simple integer and floating-point values, use C-compatible types:

c_char
c_short
c_int
c_long
c_longlong
c_float
c_double

These names follow the target C ABI.

For structs, use extern struct.

const Point = extern struct {
    x: c_int,
    y: c_int,
};

This matches the usual C declaration:

struct Point {
    int x;
    int y;
};

A normal Zig struct should not be passed to C as a C struct.

const BadPoint = struct {
    x: i32,
    y: i32,
};

Zig may choose a layout that is useful for Zig. C cannot rely on it.

The same rule applies to unions.

const Number = extern union {
    i: c_int,
    d: c_double,
};

This matches:

union Number {
    int i;
    double d;
};

For packed binary formats, use packed struct, but do not confuse it with C layout. Packed layout is for bit-level representation. C struct layout is ABI layout.

const Flags = packed struct {
    read: bool,
    write: bool,
    exec: bool,
};

This is useful for exact bits. It is not a general replacement for extern struct.

Function layout is controlled by the calling convention.

extern "c" fn puts(s: [*:0]const u8) c_int;

The "c" convention tells Zig to call the function as C would call it on the target platform.

When exporting a function to C, keep the boundary plain:

export fn scale(x: c_double, factor: c_double) c_double {
    return x * factor;
}

Avoid Zig-only parameter types in exported functions:

export fn bad(xs: []const u8) void {
    _ = xs;
}

A slice has Zig layout. C does not know it. Use pointer plus length:

export fn good(ptr: [*]const u8, len: usize) void {
    const xs = ptr[0..len];
    _ = xs;
}

At the boundary, prefer boring types.

Inside Zig, convert them into safer Zig forms. Keep C layout at the edge.