zig-cinterop — Claude Code skill for Zig and C interop
A skill that walks the agent through Zig–C interop: @cImport, translate-c, extern struct, exporting Zig functions and mixed builds.
npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a claude-codeWhat zig-cinterop does
The skill fires when @cImport or @cInclude shows up, when you call a C function from Zig, hand a Zig function to C, or want to see what translate-c produced from a header. The agent gets a type mapping table (int → c_int, size_t → usize, char * → [*:0]u8), plus examples of extern struct for matching a C struct layout and packed struct for bitfields and wire headers.
Nine scenarios are covered with working snippets: importing headers with @cDefine and @cUndef, passing null-terminated strings, opaque types like FILE, variadic functions, and exporting through export fn or callconv(.C). There is a build.zig section too. linkLibC() is required when you call C functions; going the other way, Zig builds as a static library that a C executable links against.
One thing to watch from the start: skip linkLibC() in build.zig and the C symbols fail to link. The skill points to references/translate-c-guide.md and to sibling skills in the same repo — zig-compiler, zig-build-system, elf-inspection, and rust-ffi if you want the Rust comparison.
What it helps with
- call a C function from Zig through
@cImport - inspect what
translate-cemits for a header - declare a C struct as
extern structwith the same layout - pack bitfields into a
packed struct - wire C and Zig together in
build.zig
Example prompts for the agent
- Help me call
snprintffrom Zig with@cImportand a null-terminated buffer - Run
translate-conmylib.hand show me howstruct MyStructcomes out - Build a static library from Zig in
build.zigand link it againstsrc/main.c
How to install zig-cinterop
1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a claude-code2Manually: put the files in place
~/.claude/skills/zig-cinterop/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/zig-cinterop/
1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a codex2Manually: put the files in place
.agents/skills/zig-cinterop/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a cursor2Manually: put the files in place
.cursor/skills/zig-cinterop/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install mohitmishra786/low-level-dev-skills zig-cinterop2Manually: put the files in place
.github/skills/zig-cinterop/SKILL.md1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a gemini-cli2Platform CLI
gemini skills install https://github.com/mohitmishra786/low-level-dev-skills --path skills/zig/zig-cinteropManually: put the files in place
.devin/skills/zig-cinterop/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about zig-cinterop
How do I install zig-cinterop in Claude?
Run npx skills add mohitmishra786/low-level-dev-skills --skill zig-cinterop -a claude-code. The skill lands at ~/.claude/skills/zig-cinterop/SKILL.md.
What does zig-cinterop do?
It guides the agent through Zig–C compatibility: header imports, reading translate-c output, ABI-compatible structs, exporting Zig functions, and mixed C/Zig builds.
How is translate-c different from @cImport?
translate-c is a command that prints Zig declarations from a C header so you can read them. In your code you normally write @cImport; translate-c is for checking what Zig sees in the API.
Source text
SKILL.md · MIT
---
name: zig-cinterop
description: Zig C interoperability skill. Use when calling C from Zig, calling Zig from C, using @cImport and @cInclude, running translate-c on C headers, defining extern structs and packed structs, matching C ABI types, or building mixed C/Zig projects. Activates on queries about @cImport, @cInclude, translate-c, extern struct, packed struct, Zig C ABI, calling C from Zig, exporting Zig to C, or bindgen equivalents.
---
# Zig C Interop
## Purpose
Guide agents through Zig's C interoperability: `@cImport`/`@cInclude` for calling C, `translate-c` for header inspection, `extern struct` and `packed struct` for ABI-compatible types, exporting Zig for C consumption, and `zig cc` for mixed C/Zig builds.
## Triggers
- "How do I call a C function from Zig?"
- "How do I use @cImport and @cInclude?"
- "How do I export Zig functions to be called from C?"
- "How do I define a struct that matches a C struct?"
- "What does translate-c do?"
- "How do I build a mixed C and Zig project?"
## Workflow
### 1. Calling C from Zig with @cImport
```zig
const c = @cImport({
@cInclude("stdio.h");
@cInclude("string.h");
@cInclude("mylib.h");
@cDefine("MY_FEATURE", "1"); // Equivalent to -DMY_FEATURE=1
@cUndef("SOME_MACRO");
});
pub fn main() void {
_ = c.printf("Hello from C: %d\n", @as(c_int, 42));
var buf: [256]u8 = undefined;
_ = c.snprintf(&buf, buf.len, "formatted: %d", @as(c_int, 100));
}
```
In `build.zig`:
```zig
exe.linkLibC(); // Required when using C functions
exe.addIncludePath(b.path("include/"));
```
### 2. translate-c — inspect C header translation
`translate-c` converts C headers to Zig declarations, letting you see exactly how Zig sees a C API:
```bash
# Translate a header file
zig translate-c /usr/include/stdio.h > stdio.zig
# Translate with defines/includes
zig translate-c -I include/ -DFEATURE=1 mylib.h > mylib.zig
# Translate and inspect specific types
zig translate-c mylib.h | grep -A5 "struct MyStruct"
```
This is Zig's equivalent of `bindgen` — you use it to understand what Zig generates, then use `@cImport` directly in code.
### 3. C type mapping
| C type | Zig type |
|--------|----------|
| `int` | `c_int` |
| `unsigned int` | `c_uint` |
| `long` | `c_long` |
| `unsigned long` | `c_ulong` |
| `long long` | `c_longlong` |
| `size_t` | `usize` |
| `ssize_t` | `isize` |
| `char *` | `[*:0]u8` (null-terminated) |
| `const char *` | `[*:0]const u8` |
| `void *` | `*anyopaque` |
| `NULL` | `null` |
| `bool` | `bool` (C99) or `c_int` (older) |
| `float` | `f32` |
| `double` | `f64` |
```zig
// Passing strings to C
const str = "hello";
_ = c.puts(str); // Zig string literals are [*:0]const u8
// Dynamic strings — need null terminator
var buf: [64:0]u8 = undefined;
const len = std.fmt.bufPrint(buf[0..63], "hello {d}", .{42}) catch unreachable;
buf[len] = 0;
_ = c.puts(&buf);
```
### 4. extern struct — ABI-compatible structs
Use `extern struct` to match a C struct's memory layout exactly:
```zig
// Matches: struct Point { int x; int y; };
const Point = extern struct {
x: c_int,
y: c_int,
};
// Matches: struct Header { uint32_t magic; uint16_t version; uint16_t flags; };
const Header = extern struct {
magic: u32,
version: u16,
flags: u16,
};
// Use with C API
var p = Point{ .x = 10, .y = 20 };
_ = c.draw_point(&p);
```
### 5. packed struct — bit-level layout
```zig
// Matches C bitfield: struct { uint8_t flags : 4; uint8_t type : 4; };
const Flags = packed struct(u8) {
mode: u4,
kind: u4,
};
// Packed struct for wire protocols
const IpHeader = packed struct(u32) {
ihl: u4,
version: u4,
tos: u8,
total_length: u16,
};
var h: IpHeader = @bitCast(@as(u32, raw_bytes));
```
### 6. Exporting Zig to C
```zig
// Export a function callable from C
export fn zig_add(a: c_int, b: c_int) c_int {
return a + b;
}
// Export with specific calling convention
pub fn my_func(x: u32) callconv(.C) u32 {
return x * 2;
}
// Export a struct (use extern struct for C layout)
export const VERSION: c_int = 42;
```
Generate a C header (manual or with tools):
```c
/* mylib.h */
#ifndef MYLIB_H
#define MYLIB_H
#include <stdint.h>
int zig_add(int a, int b);
uint32_t my_func(uint32_t x);
extern int VERSION;
#endif
```
### 7. Calling Zig from C in build.zig
```zig
// Build Zig as a C-compatible static library
const lib = b.addStaticLibrary(.{
.name = "myzig",
.root_source_file = b.path("src/lib.zig"),
.target = target,
.optimize = optimize,
});
// The C code that uses the Zig library
const c_exe = b.addExecutable(.{
.name = "c_consumer",
.target = target,
.optimize = optimize,
});
c_exe.addCSourceFile(.{
.file = b.path("src/main.c"),
.flags = &.{"-std=c11"},
});
c_exe.linkLibrary(lib);
c_exe.linkLibC();
b.installArtifact(c_exe);
```
### 8. Opaque types and forward declarations
```zig
// Forward-declared C struct (opaque)
const FILE = opaque {};
extern fn fopen(path: [*:0]const u8, mode: [*:0]const u8) ?*FILE;
extern fn fclose(file: *FILE) c_int;
extern fn fprintf(file: *FILE, fmt: [*:0]const u8, ...) c_int;
// Opaque handle pattern
const MyHandle = opaque {};
extern fn lib_create() ?*MyHandle;
extern fn lib_destroy(h: *MyHandle) void;
```
### 9. Variadic functions
```zig
// Call variadic C functions using @call with variadic args
const c = @cImport(@cInclude("stdio.h"));
// printf works directly through @cImport
_ = c.printf("value: %d\n", @as(c_int, 42));
// For custom variadic C functions, use extern with ...
extern fn my_log(level: c_int, fmt: [*:0]const u8, ...) void;
```
For translate-c output guide and C ABI types reference, see [references/translate-c-guide.md](references/translate-c-guide.md).
## Related skills
- Use `skills/zig/zig-compiler` for `zig cc` C compilation and basic Zig builds
- Use `skills/zig/zig-build-system` for `build.zig` with mixed C/Zig projects
- Use `skills/binaries/elf-inspection` to verify symbol exports and ABI
- Use `skills/rust/rust-ffi` for comparison with Rust's C FFI approach
© mohitmishra786 · MIT · mohitmishra786/low-level-dev-skills