# `@cImport`

### `@cImport`

`@cImport` is Zig’s built-in way to import C declarations from header files.

A C header file describes functions, structs, constants, enums, and macros. Zig can read that header, translate the declarations, and make them available inside Zig code.

For example, this imports the C standard I/O header:

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

After that, you can call C functions through `c`:

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

This is the basic shape:

```zig
const c = @cImport({
    @cInclude("some_header.h");
});
```

The imported C API becomes available under the name `c`.

You could choose another name:

```zig
const libc = @cImport({
    @cInclude("stdio.h");
});
```

Then you call:

```zig
_ = libc.puts("Hello");
```

The name is not special. `c` is just a common convention.

#### Why `@cImport` Exists

Many C libraries expose their API through header files.

A header might contain this:

```c
// mathlib.h

int add(int a, int b);
int sub(int a, int b);
```

Without `@cImport`, you would need to manually rewrite these declarations in Zig. That would be tedious and error-prone.

With `@cImport`, Zig can read the C header directly:

```zig
const c = @cImport({
    @cInclude("mathlib.h");
});
```

Then Zig can call:

```zig
const x = c.add(10, 20);
const y = c.sub(10, 3);
```

This reduces duplicate work. The C header remains the source of truth.

#### `@cImport` Runs at Compile Time

`@cImport` happens while Zig is compiling your program.

That is important.

The compiler reads the C header, translates the declarations, and checks your Zig code against those declarations. If you call a C function with the wrong number of arguments or a wrong type, the compiler can often catch it.

For example, suppose C declares:

```c
int add(int a, int b);
```

This Zig call is correct:

```zig
const x = c.add(1, 2);
```

This call is wrong:

```zig
const x = c.add(1);
```

The function expects two arguments. Zig knows that because it imported the C declaration.

#### `@cInclude`

Inside `@cImport`, you usually use `@cInclude`.

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

This is similar to writing this in C:

```c
#include <stdio.h>
```

But in Zig, the include is part of a compile-time block.

You can include more than one header:

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

Now functions from all three headers are available through `c`.

Example:

```zig
const len = c.strlen("hello");
_ = c.puts("done");
```

#### `@cDefine`

Some C headers change behavior depending on preprocessor macros.

In C, you might write:

```c
#define SOME_FEATURE 1
#include "library.h"
```

In Zig, you can do this:

```zig
const c = @cImport({
    @cDefine("SOME_FEATURE", "1");
    @cInclude("library.h");
});
```

The macro is defined before the header is imported.

This is useful when a C library has optional features controlled by macros.

For example:

```zig
const c = @cImport({
    @cDefine("RAYGUI_IMPLEMENTATION", "");
    @cInclude("raygui.h");
});
```

The exact macro names depend on the C library.

#### `@cUndef`

You can also undefine a macro:

```zig
const c = @cImport({
    @cDefine("DEBUG", "1");
    @cUndef("DEBUG");
    @cInclude("library.h");
});
```

This is less common, but it can be useful when a header behaves differently depending on whether a macro exists.

#### A Complete Example

Suppose we have this C header:

```c
// mathlib.h

#ifndef MATHLIB_H
#define MATHLIB_H

int add(int a, int b);

#endif
```

And this C source file:

```c
// mathlib.c

#include "mathlib.h"

int add(int a, int b) {
    return a + b;
}
```

Now we can call it from Zig:

```zig
const std = @import("std");

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

pub fn main() void {
    const result = c.add(10, 20);
    std.debug.print("result = {}\n", .{result});
}
```

This prints:

```text
result = 30
```

But importing the header is not enough. Zig also needs the implementation from `mathlib.c`.

The header says the function exists.

The C source file contains the function body.

Your build must include both.

#### Header Import vs Linking

This is a common beginner mistake.

`@cImport` imports declarations. It does not magically link the C library.

For example:

```zig
const c = @cImport({
    @cInclude("sqlite3.h");
});
```

This lets Zig understand names such as `sqlite3_open`, `sqlite3_close`, and `sqlite3_exec`.

But your final program still needs to link SQLite.

If the library is not linked, the compiler may understand the function call, but the linker will fail because it cannot find the actual compiled function.

The rule is:

```text
@cImport gives Zig the C declarations.
Linking gives the final executable the C implementation.
```

Both are required.

#### Imported Names

When Zig imports a C header, the declarations become fields on the imported namespace.

For example:

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

You access imported names like this:

```zig
c.puts
c.printf
c.FILE
c.fopen
c.fclose
```

This keeps C names grouped together. It also makes the boundary obvious. When you see `c.puts`, you know this call goes to C.

You should usually keep that boundary visible.

Avoid this style:

```zig
const puts = c.puts;
```

It can be convenient, but it hides where the function comes from. In beginner code, prefer `c.puts`.

#### C Integers in Zig

C types are not always exactly the same as Zig types.

For example, C `int` is usually imported as `c_int`.

A C function like this:

```c
int add(int a, int b);
```

is usually seen by Zig as something like:

```zig
fn add(a: c_int, b: c_int) c_int
```

So this works:

```zig
const result: c_int = c.add(10, 20);
```

Zig provides C-compatible type names such as:

| C type | Zig C-compatible type |
|---|---|
| `char` | `c_char` |
| `short` | `c_short` |
| `int` | `c_int` |
| `long` | `c_long` |
| `long long` | `c_longlong` |
| `unsigned int` | `c_uint` |
| `float` | `f32` |
| `double` | `f64` |

Use these C-compatible types at the C boundary.

Inside normal Zig code, you can convert to ordinary Zig types when needed.

#### C Strings

Many C functions expect null-terminated strings.

For example:

```c
int puts(const char *s);
```

The string ends when C finds a zero byte.

This Zig call works:

```zig
_ = c.puts("hello");
```

String literals in Zig can be passed to C functions that expect null-terminated strings.

But be careful with ordinary slices:

```zig
const msg: []const u8 = "hello";
```

A slice has a pointer and a length. A C function that takes `const char *` does not know the length. It keeps reading until it finds a zero byte.

This can be unsafe:

```zig
_ = c.puts(msg.ptr);
```

The pointer alone does not prove that the data is null-terminated.

When working with C strings, keep this distinction clear:

| Zig value | Meaning |
|---|---|
| `[]const u8` | Pointer plus length |
| `[*:0]const u8` | Pointer to bytes ending in zero |
| `const char *` in C | Pointer to bytes ending in zero |

C strings need a sentinel zero byte.

#### C Structs

C structs can be imported too.

Suppose a header has this:

```c
typedef struct Point {
    int x;
    int y;
} Point;
```

After importing the header, Zig can use the type:

```zig
const c = @cImport({
    @cInclude("point.h");
});

pub fn main() void {
    var p = c.Point{
        .x = 10,
        .y = 20,
    };

    _ = p;
}
```

The imported struct keeps a C-compatible layout.

This matters because C and Zig must agree on where fields live in memory.

#### C Enums and Constants

C headers often define enums and constants:

```c
#define MODE_READ 1
#define MODE_WRITE 2

enum Status {
    STATUS_OK = 0,
    STATUS_FAILED = 1,
};
```

After import, you may access them through `c`:

```zig
const mode = c.MODE_READ;
const status = c.STATUS_OK;
```

The exact imported form can vary depending on how the C header defines the names. C macros are especially tricky because not every macro maps cleanly to Zig.

Simple numeric macros usually work well.

Complex function-like macros may not.

#### Macros Are the Hard Part

C macros are not real functions. They are preprocessor substitutions.

Example:

```c
#define SQUARE(x) ((x) * (x))
```

This may look like a function, but it is not. It is expanded by the C preprocessor before compilation.

Zig can import some macros, especially simple constants, but complex macros may not translate cleanly.

When a macro does not import well, you usually write a small C wrapper function.

For example:

```c
// wrapper.h

int square_int(int x);
```

```c
// wrapper.c

#define SQUARE(x) ((x) * (x))

int square_int(int x) {
    return SQUARE(x);
}
```

Then Zig imports the wrapper:

```zig
const c = @cImport({
    @cInclude("wrapper.h");
});

const y = c.square_int(9);
```

This is often cleaner than fighting with complicated C macros.

#### Include Paths

If the header is in the same directory, this may work:

```zig
@cInclude("mathlib.h");
```

If the header is somewhere else, the build must tell Zig where to search.

In `build.zig`, that usually means adding an include path to the compile step.

Conceptually:

```text
Tell Zig where the header files are.
Then @cInclude can find them.
```

If Zig cannot find the header, you will get an error similar to:

```text
'header.h' file not found
```

The fix is not inside `@cImport` itself. The fix is usually in the build configuration.

#### Keep C Imports Small

A common pattern is to put all C imports in one Zig file.

For example:

```zig
// c.zig

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

Then other Zig files can import that file:

```zig
const c = @import("c.zig").c;
```

This keeps C imports centralized.

For small programs, putting `@cImport` directly in `main.zig` is fine.

For larger programs, a dedicated C import module is cleaner.

#### Wrap C APIs in Zig APIs

Direct C imports are useful, but most of your program should not need to deal with raw C details.

Instead of spreading this everywhere:

```zig
const rc = c.some_library_do_work(ptr, len);
if (rc != 0) return error.Failed;
```

you can write a Zig wrapper:

```zig
fn doWork(buffer: []u8) !void {
    const rc = c.some_library_do_work(buffer.ptr, buffer.len);

    if (rc != 0) {
        return error.Failed;
    }
}
```

Now the rest of your code calls:

```zig
try doWork(buffer);
```

That is easier to read and safer to maintain.

The C boundary remains in one place.

#### What to Remember

`@cImport` imports C declarations into Zig.

It usually contains `@cInclude`.

It can also contain `@cDefine` and `@cUndef`.

It happens at compile time.

It does not replace linking.

It works best when C headers are clean and simple.

C strings, macros, memory ownership, and pointer rules still need care.

The best pattern is to use `@cImport` at the boundary, then wrap the imported C API in normal Zig functions.

