# ABI

> extern and packed, for layouts you can rely on.

```zig
const std = @import("std");
const expect = std.testing.expect;

// `extern struct` guarantees C layout: declaration order, C padding rules.
const CPoint = extern struct {
    x: i32,
    y: i32,
};

// `packed struct` is bit-level and backed by an integer, with no padding.
const Flags = packed struct {
    a: bool,
    b: bool,
    rest: u6,
};

// A plain struct may be reordered and padded however the compiler likes.
const Loose = struct {
    small: u8,
    big: u64,
};

test "extern struct follows C layout" {
    try expect(@sizeOf(CPoint) == 8);
    try expect(@offsetOf(CPoint, "x") == 0);
    try expect(@offsetOf(CPoint, "y") == 4);
}

test "packed struct is exactly its bits" {
    try expect(@bitSizeOf(Flags) == 8);
    try expect(@sizeOf(Flags) == 1);

    const f = Flags{ .a = true, .b = false, .rest = 0 };
    // Packed structs convert to their backing integer.
    try expect(@as(u8, @bitCast(f)) == 1);
}

test "plain structs make no layout promise" {
    // Zig is free to order these for packing, so do not assume offsets.
    try expect(@sizeOf(Loose) >= 9);
}

test "extern union and enum" {
    const E = extern union { i: i32, f: f32 };
    try expect(@sizeOf(E) == 4);

    // An enum with a C ABI tag type.
    const Colour = enum(c_int) { red, green, blue };
    try expect(@intFromEnum(Colour.green) == 1);
}

// `callconv(.c)` makes a Zig function callable from C.
export fn addFromC(a: c_int, b: c_int) callconv(.c) c_int {
    return a + b;
}

test "exported function is callable from Zig too" {
    try expect(addFromC(2, 3) == 5);
}
```

*Runnable: compiled to WebAssembly and executed by CI against Zig master. (`05-working-with-c.abi`)*

A plain Zig `struct` makes **no layout promise**. Fields may be reordered and
padded however the compiler prefers. That is a feature (it lets the optimiser
pack things), but it means you cannot hand one to C.

## The three layouts

| Declaration | Layout | Use for |
| --- | --- | --- |
| `struct` | unspecified | ordinary Zig code |
| `extern struct` | C ABI: declaration order, C padding | C interop |
| `packed struct` | exact bits, no padding, integer-backed | wire formats, registers |

```zig
const CPoint = extern struct { x: i32, y: i32 };   // offsets 0 and 4
const Flags = packed struct { a: bool, b: bool, rest: u6 };  // exactly 8 bits
```

A `packed struct` converts to and from its backing integer with `@bitCast`,
which makes it the natural way to describe hardware registers and protocol
headers.

## Verifying, not assuming

`@sizeOf`, `@offsetOf`, and `@bitSizeOf` are comptime values, so a layout
assumption can be asserted at compile time:

```zig
comptime {
    std.debug.assert(@offsetOf(CPoint, "y") == 4);
}
```

Worth doing at any real C boundary: it turns a silent mismatch into a build
failure.

## Calling conventions

`callconv(.c)` gives a function the C calling convention, and `export` gives it
an unmangled symbol name:

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

That is all it takes for C to call Zig. Going the other way, declare the C
function with `extern`:

```zig
extern "c" fn abs(n: c_int) c_int;
```

## Do not `extern` everything

`extern struct` costs you the packing the optimiser would otherwise do. Use it
at the boundary, and use ordinary structs inside.
