---
title: "Zig e C: Interoperabilidade Completa - Guia Prático"
url: "https://ziglang.com.br/tutoriais/zig-c-interoperabilidade/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-c-interoperabilidade.MD"
description: "Aprenda como chamar código C de Zig, usar bibliotecas C existentes, traduzir headers automaticamente e gerenciar memória entre fronteiras. Tutorial completo com exemplos práticos."
date: "2026-02-10"
author: ""
---

# Zig e C: Interoperabilidade Completa - Guia Prático

Aprenda como chamar código C de Zig, usar bibliotecas C existentes, traduzir headers automaticamente e gerenciar memória entre fronteiras. Tutorial completo com exemplos práticos.


A interoperabilidade com C é uma das features mais poderosas de Zig. Diferente de outras linguagens que exigem bindings manuais, ferramentas externas ou camadas complexas de abstração, Zig permite **importar headers C diretamente** e trabalhar com código C nativamente. Neste guia completo, vamos explorar todas as técnicas de interoperabilidade Zig-C com exemplos práticos.

## Por Que a Interoperabilidade C é Importante

### O Legado C é Imenso

O ecossistema C representa décadas de desenvolvimento de software:

- **Sistemas operacionais**: Linux kernel, Windows APIs, POSIX
- **Bibliotecas fundamentais**: OpenSSL, zlib, SQLite, libpng
- **Drivers e hardware**: Praticamente todo driver de dispositivo
- **Protocolos de rede**: TCP/IP, HTTP/2, QUIC implementações
- **Criptografia**: OpenSSL, libsodium, GnuTLS
- **Compressão**: zlib, bzip2, lz4

Reescrever tudo isso em Zig seria inviável. A solução: **usar diretamente**.

### Zig como Substituto de Compilador C

Zig pode não apenas chamar código C, mas também **compilar código C**:

```bash
# Zig como substituto do gcc/clang
zig cc main.c -o programa
zig c++ main.cpp -o programa_cpp

# Cross-compilation trivial
zig cc -target aarch64-linux-gnu main.c -o programa_arm
```

Isso significa que você pode usar Zig como toolchain completa para projetos C existentes.

## Fundamentos: Chamando Funções C

### O Básico: @cImport e @cInclude

A magia começa com `@cImport`, que importa definições C diretamente para Zig:

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

pub fn main() void {
    _ = c.printf("Hello do C!\n");
}
```

O que acontece aqui:

1. `@cInclude("stdio.h")` diz ao compilador para processar o header C
2. `@cImport` converte as definições C em declarações Zig
3. `c` se torna um namespace contendo todas as definições do header

### Exemplo Prático: Usando a Libc

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

pub fn main() !void {
    // printf
    _ = c.printf("=== Testando Libc ===\n");
    
    // malloc/free
    const ptr = c.malloc(100);
    if (ptr == null) {
        return error.OutOfMemory;
    }
    defer c.free(ptr); // Liberamos a memória C com free C!
    
    // strcpy
    const msg: [*c]u8 = @ptrCast(ptr);
    _ = c.strcpy(msg, "Olá do Zig!");
    _ = c.printf("Mensagem: %s\n", msg);
    
    // funções matemáticas
    const raiz = c.sqrt(2.0);
    _ = c.printf("Raiz de 2: %f\n", raiz);
    
    // random
    c.srand(@intCast(c.time(null)));
    const aleatorio = c.rand();
    _ = c.printf("Número aleatório: %d\n", aleatorio);
}
```

**Nota importante**: Zig converte automaticamente:
- `int` → `c_int`
- `char*` → `[*c]u8` (ponteiro C para u8)
- `double` → `f64`
- `void*` → `*anyopaque`

## Tipos C em Zig

### Tabela de Conversão de Tipos

Ao importar código C, Zig converte os tipos automaticamente:

| Tipo C | Tipo Zig | Observação |
|--------|----------|------------|
| `char` | `c_char` | Pode ser `i8` ou `u8` dependendo da plataforma |
| `short` | `c_short` | Geralmente `i16` |
| `int` | `c_int` | Geralmente `i32` |
| `long` | `c_long` | Depende da plataforma (i32 ou i64) |
| `long long` | `c_longlong` | Geralmente `i64` |
| `float` | `f32` | Ponto flutuante 32-bit |
| `double` | `f64` | Ponto flutuante 64-bit |
| `void*` | `*anyopaque` | Ponteiro para tipo opaco |
| `char*` | `[*c]u8` | Ponteiro C nulo-terminado |
| `const char*` | `[*c]const u8` | Ponteiro C constante |
| `size_t` | `c_size_t` | Tipo de tamanho da plataforma |
| `FILE*` | `*c.FILE` | Ponteiro para struct FILE |
| `struct X` | `c.struct_X` | Struct com prefixo |
| `enum X` | `c.enum_X` | Enum com prefixo |
| `union X` | `c.union_X` | Union com prefixo |
| `typedef` | Preservado | O alias é mantido |

### Ponteiros C vs Ponteiros Zig

A diferença mais importante:

```zig
// Ponteiro Zig: NUNCA nulo, bounds checking
var ptr_zig: *i32 = &valor;

// Ponteiro C: Pode ser nulo, sem bounds checking
var ptr_c: [*c]i32 = c.malloc(100);
if (ptr_c == null) { /* erro */ }
```

**Padrão idiomático**: Converta para slices Zig quando possível:

```zig
const c = @cImport(@cInclude("stdlib.h"));

// Alocação C
const ptr_c = c.malloc(100 * @sizeOf(i32));
if (ptr_c == null) return error.OutOfMemory;
defer c.free(ptr_c);

// Converter para slice Zig (seguro!)
const slice: []i32 = @as([*]i32, @ptrCast(ptr_c))[0..100];
slice[0] = 42; // Agora temos bounds checking!
slice[50] = 100;
```

## Importando Bibliotecas C Externas

### Configurando o build.zig

Para usar uma biblioteca C, você precisa configurar o `build.zig`:

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

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "meu_programa",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // === Configurações para bibliotecas C ===
    
    // 1. Adicionar caminho de includes
    exe.addIncludePath(b.path("include/"));
    
    // 2. Adicionar caminho de bibliotecas
    exe.addLibraryPath(b.path("lib/"));
    
    // 3. Linkar com bibliotecas específicas
    exe.linkSystemLibrary("sqlite3");    // -lsqlite3
    exe.linkSystemLibrary("curl");       // -lcurl
    exe.linkSystemLibrary("ssl");        // -lssl
    exe.linkSystemLibrary("crypto");     // -lcrypto
    
    // 4. Linkar com a libc (necessário para a maioria das libs C)
    exe.linkLibC();

    b.installArtifact(exe);
}
```

### Exemplo Completo: Usando SQLite

```zig
// src/main.zig
const std = @import("std");
const c = @cImport({
    @cInclude("sqlite3.h");
});

const DatabaseError = error{
    OpenFailed,
    QueryFailed,
    PrepareFailed,
};

const Database = struct {
    db: ?*c.sqlite3,

    pub fn open(path: []const u8) DatabaseError!Database {
        var db: ?*c.sqlite3 = null;
        
        // Converter slice Zig para ponteiro C nulo-terminado
        const c_path = @ptrCast([*c]const u8, path.ptr);
        
        const rc = c.sqlite3_open(c_path, &db);
        if (rc != c.SQLITE_OK) {
            std.debug.print("Erro ao abrir DB: {s}\n", .{c.sqlite3_errmsg(db)});
            return DatabaseError.OpenFailed;
        }
        
        return Database{ .db = db };
    }

    pub fn close(self: *Database) void {
        if (self.db) |db| {
            _ = c.sqlite3_close(db);
            self.db = null;
        }
    }

    pub fn execute(self: Database, sql: []const u8) DatabaseError!void {
        const c_sql = @ptrCast([*c]const u8, sql.ptr);
        const rc = c.sqlite3_exec(self.db, c_sql, null, null, null);
        
        if (rc != c.SQLITE_OK) {
            return DatabaseError.QueryFailed;
        }
    }
};

pub fn main() !void {
    var db = try Database.open(":memory:");
    defer db.close();

    try db.execute(
        \\CREATE TABLE users (
        \\  id INTEGER PRIMARY KEY,
        \\  name TEXT NOT NULL
        \\);
    );

    try db.execute(
        \\INSERT INTO users (name) VALUES ('Alice'), ('Bob');
    );

    std.debug.print("Banco de dados criado com sucesso!\n", .{});
}
```

```zig
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "sqlite_demo",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Linkar com SQLite
    exe.linkSystemLibrary("sqlite3");
    exe.linkLibC();

    b.installArtifact(exe);
}
```

## Convertendo Strings entre C e Zig

### C String → Zig Slice

```zig
const std = @import("std");
const c = @cImport(@cInclude("string.h"));

// Ponteiro C para string
const c_string: [*c]const u8 = c.getenv("HOME");

// Método 1: Calcular length com strlen
const len = c.strlen(c_string);
const zig_slice: []const u8 = c_string[0..len];

// Método 2: Usar std.mem.span (mais idiomático)
const zig_slice2: [:0]const u8 = std.mem.span(c_string);
// ^ [:0] significa "slice nulo-terminado"
```

### Zig Slice → C String

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

// Slice Zig normal
const zig_string: []const u8 = "Olá, C!";

// Método 1: Garantir nulo no final (se já for string literal)
const c_string: [*c]const u8 = "Olá, C!"; // String literals já são nulo-terminadas

// Método 2: Alocar com nulo para slices dinâmicos
fn toCString(allocator: std.mem.Allocator, slice: []const u8) ![:0]u8 {
    // Aloca espaço extra para o nulo
    var result = try allocator.allocSentinel(u8, slice.len, 0);
    @memcpy(result[0..slice.len], slice);
    return result;
}

// Uso
const c_str = try toCString(allocator, zig_string);
defer allocator.free(c_str);
```

### Exemplo Prático: Wrapping uma Função C

```zig
const std = @import("std");
const c = @cImport({
    @cInclude("curl/curl.h");
});

// Wrapper idiomático em Zig
pub fn fetchUrl(allocator: std.mem.Allocator, url: []const u8) ![]u8 {
    const curl = c.curl_easy_init();
    if (curl == null) return error.CurlInitFailed;
    defer c.curl_easy_cleanup(curl);
    
    // Converter URL para C string
    const c_url = try allocator.dupeZ(u8, url); // dupeZ = duplicar com zero no final
    defer allocator.free(c_url);
    
    // Configurar CURL
    _ = c.curl_easy_setopt(curl, c.CURLOPT_URL, c_url.ptr);
    _ = c.curl_easy_setopt(curl, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 1));
    
    // Executar
    const res = c.curl_easy_perform(curl);
    if (res != c.CURLE_OK) {
        return error.CurlRequestFailed;
    }
    
    return "Requisição realizada com sucesso";
}
```

## Trabalhando com Structs C

### Mapeando Structs

```c
// mylib.h
typedef struct {
    int x;
    int y;
    char name[64];
    void* user_data;
} Point;

Point* point_create(int x, int y);
void point_destroy(Point* p);
void point_move(Point* p, int dx, int dy);
```

```zig
// main.zig
const std = @import("std");
const c = @cImport({
    @cInclude("mylib.h");
});

// O struct é importado como c.Point
pub fn main() void {
    // Criar instância
    const p = c.point_create(10, 20);
    if (p == null) {
        std.debug.print("Falha ao criar ponto\n", .{});
        return;
    }
    defer c.point_destroy(p);
    
    // Acessar campos
    std.debug.print("Ponto: x={}, y={}\n", .{ p.?.x, p.?.y });
    
    // Modificar
    c.point_move(p, 5, -3);
    std.debug.print("Após mover: x={}, y={}\n", .{ p.?.x, p.?.y });
    
    // Acessar array
    const name_slice = &p.?.name;
    std.debug.print("Nome: {s}\n", .{std.mem.sliceTo(name_slice, 0)});
}
```

### Criando Wrappers Idiomáticos

```zig
const Point = struct {
    raw: *c.Point,
    
    pub fn new(x: c_int, y: c_int) !Point {
        const raw = c.point_create(x, y);
        if (raw == null) return error.OutOfMemory;
        return Point{ .raw = raw };
    }
    
    pub fn deinit(self: Point) void {
        c.point_destroy(self.raw);
    }
    
    pub fn move(self: Point, dx: c_int, dy: c_int) void {
        c.point_move(self.raw, dx, dy);
    }
    
    pub fn x(self: Point) c_int {
        return self.raw.x;
    }
    
    pub fn y(self: Point) c_int {
        return self.raw.y;
    }
};

// Uso idiomático em Zig
pub fn main() !void {
    const p = try Point.new(10, 20);
    defer p.deinit();
    
    p.move(5, -3);
    std.debug.print("Posição: ({}, {})\n", .{ p.x(), p.y() });
}
```

## Callbacks e Funções de Ponteiro

### Passando Funções Zig para C

Muitas bibliotecas C usam callbacks. Em Zig, você pode passar funções diretamente:

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

// Função de comparação para qsort
fn compareInts(a: ?*const anyopaque, b: ?*const anyopaque) callconv(.C) c_int {
    const a_int: *const c_int = @ptrCast(@alignCast(a));
    const b_int: *const c_int = @ptrCast(@alignCast(b));
    
    if (a_int.* < b_int.*) return -1;
    if (a_int.* > b_int.*) return 1;
    return 0;
}

pub fn main() void {
    var nums = [_]c_int{ 5, 2, 8, 1, 9, 3 };
    
    c.qsort(
        &nums,
        nums.len,
        @sizeOf(c_int),
        compareInts  // Passamos a função Zig como callback C!
    );
    
    // nums agora está ordenado: {1, 2, 3, 5, 8, 9}
}
```

**Importante**: Use `callconv(.C)` para funções que serão chamadas por código C.

### Recebendo Callbacks C em Zig

```zig
// Simulando uma biblioteca C que chama callbacks
typedef void (*ProgressCallback)(int percent, void* user_data);
void do_work(ProgressCallback cb, void* user_data);
```

```zig
const c = @cImport(@cInclude("worker.h"));

const Context = struct {
    total_items: usize,
    processed: usize,
};

fn onProgress(percent: c_int, user_data: ?*anyopaque) callconv(.C) void {
    const ctx: *Context = @ptrCast(@alignCast(user_data));
    std.debug.print("Progresso: {}% ({}/{} items)\n", .{
        percent,
        ctx.processed,
        ctx.total_items
    });
}

pub fn main() void {
    var ctx = Context{
        .total_items = 100,
        .processed = 0,
    };
    
    c.do_work(onProgress, &ctx);
}
```

## Gerenciamento de Memória Entre Fronteiras

### Regras Fundamentais

1. **Quem aloca, libera**: Se C alocou com `malloc`, libere com `free`
2. **Se Zig alocou, Zig libera**: Use o mesmo allocator
3. **Não misture allocators**: Não passe memória Zig para `free` C

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

// ❌ ERRADO: Misturando allocators
pub fn wrong() void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    
    const zig_mem = try allocator.alloc(u8, 100);
    c.free(zig_mem); // ERRO! zig_mem não foi alocado com malloc
}

// ✅ CORRETO: Cada lado gerencia sua memória
pub fn correct() !void {
    // Memória C
    const c_mem = c.malloc(100);
    if (c_mem == null) return error.OutOfMemory;
    defer c.free(c_mem); // Libera com free
    
    // Memória Zig
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    
    const zig_mem = try allocator.alloc(u8, 100);
    defer allocator.free(zig_mem); // Libera com allocator
}
```

### Padrão: Allocator Zig + Free C

Quando você precisa que C libere memória que veio de Zig:

```zig
// Alocar com allocator Zig
const buffer = try allocator.alloc(u8, size);

// Passar para C (C não deve liberar!)
c.some_function(buffer.ptr);

// Liberar no Zig quando C terminar
allocator.free(buffer);
```

### Padrão: Arena Allocator para C

Para situações complexas, use um arena allocator:

```zig
pub fn processWithC(allocator: std.mem.Allocator) !void {
    // Criar arena
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();
    const arena_allocator = arena.allocator();
    
    // Alocar tudo que C precisa na arena
    const buf1 = try arena_allocator.alloc(u8, 100);
    const buf2 = try arena_allocator.alloc(u8, 200);
    const buf3 = try arena_allocator.dupe(u8, "dados");
    
    // Passar para C
    c.process_data(buf1.ptr, buf2.ptr, buf3.ptr);
    
    // Toda a memória é liberada de uma vez no defer
}
```

## Traduzindo C para Zig Automaticamente

### zig translate-c

Zig pode traduzir código C inteiro para Zig automaticamente:

```bash
# Traduzir um header
zig translate-c mylib.h > mylib.zig

# Traduzir com defines
zig translate-c -DMY_DEFINE=1 mylib.h

# Traduzir com includes específicos
zig translate-c -I/usr/local/include mylib.h
```

### Exemplo de Tradução

Código C:
```c
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

typedef struct {
    double real;
    double imag;
} Complex;

Complex complex_add(Complex a, Complex b);
Complex complex_mul(Complex a, Complex b);
double complex_magnitude(Complex c);

#endif
```

Após `zig translate-c math_utils.h`:

```zig
// math_utils.zig (gerado automaticamente)
pub const Complex = extern struct {
    real: f64,
    imag: f64,
};

pub extern fn complex_add(a: Complex, b: Complex) Complex;
pub extern fn complex_mul(a: Complex, b: Complex) Complex;
pub extern fn complex_magnitude(c: Complex) f64;
```

### Integrando Código Traduzido

```zig
// Em vez de @cImport, use @import no arquivo traduzido
const math = @import("math_utils.zig");

pub fn main() void {
    const a = math.Complex{ .real = 1.0, .imag = 2.0 };
    const b = math.Complex{ .real = 3.0, .imag = 4.0 };
    
    const sum = math.complex_add(a, b);
    std.debug.print("Soma: {} + {}i\n", .{ sum.real, sum.imag });
    
    const mag = math.complex_magnitude(a);
    std.debug.print("Magnitude: {}\n", .{mag});
}
```

## Compilando Código C com Zig

### Usando zig cc

Zig pode substituir completamente gcc/clang:

```bash
# Compilar arquivo C
zig cc main.c -o programa

# Compilar múltiplos arquivos
zig cc main.c utils.c -o programa -I./include -L./lib -lmylib

# Com otimização
zig cc -O2 main.c -o programa

# Com símbolos de debug
zig cc -g main.c -o programa
```

### Cross-compilation Fácil

```bash
# Compilar para Windows a partir do Linux/macOS
zig cc -target x86_64-windows-gnu main.c -o programa.exe

# Compilar para ARM Linux
zig cc -target aarch64-linux-gnu main.c -o programa_arm

# Compilar para WebAssembly
zig cc -target wasm32-wasi main.c -o programa.wasm

# Compilar para macOS a partir do Linux
zig cc -target x86_64-macos-none main.c -o programa_mac
```

### Integrando C no build.zig

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

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "mixed_project",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Adicionar código C ao projeto
    exe.addCSourceFile(.{
        .file = b.path("src/legacy.c"),
        .flags = &.{"-std=c11", "-Wall"},
    });
    
    exe.addCSourceFile(.{
        .file = b.path("src/utils.c"),
        .flags = &.{"-O2"},
    });

    // Headers
    exe.addIncludePath(b.path("include/"));
    
    exe.linkLibC();
    b.installArtifact(exe);
}
```

## Projeto Prático: Wrapper de Biblioteca C

Vamos criar um wrapper completo para uma biblioteca C hipotética de logging:

### Biblioteca C (clog.h / clog.c)

```c
// clog.h
#ifndef CLOG_H
#define CLOG_H

typedef enum {
    LOG_DEBUG = 0,
    LOG_INFO = 1,
    LOG_WARN = 2,
    LOG_ERROR = 3
} LogLevel;

typedef struct Logger Logger;

Logger* logger_new(const char* name, LogLevel min_level);
void logger_free(Logger* logger);
void logger_log(Logger* logger, LogLevel level, const char* message);
void logger_set_level(Logger* logger, LogLevel level);

#endif
```

### Wrapper Zig Idiomático

```zig
// src/clog.zig
const std = @import("std");
const c = @cImport(@cInclude("clog.h"));

pub const Level = enum(c_int) {
    debug = c.LOG_DEBUG,
    info = c.LOG_INFO,
    warn = c.LOG_WARN,
    error = c.LOG_ERROR,
};

pub const Logger = struct {
    raw: *c.Logger,
    name: []const u8,

    const Self = @This();

    pub fn new(name: []const u8, min_level: Level) !Self {
        const c_name = @ptrCast([*c]const u8, name.ptr);
        const raw = c.logger_new(c_name, @intFromEnum(min_level));
        
        if (raw == null) return error.OutOfMemory;
        
        return Self{
            .raw = raw.?,  // Unwrap optional
            .name = name,
        };
    }

    pub fn deinit(self: Self) void {
        c.logger_free(self.raw);
    }

    pub fn log(self: Self, level: Level, comptime fmt: []const u8, args: anytype) void {
        var buf: [1024]u8 = undefined;
        const message = std.fmt.bufPrint(&buf, fmt, args) catch |err| {
            std.debug.print("Erro ao formatar log: {}\n", .{err});
            return;
        };
        
        const c_msg = @ptrCast([*c]const u8, message.ptr);
        c.logger_log(self.raw, @intFromEnum(level), c_msg);
    }

    pub fn debug(self: Self, comptime fmt: []const u8, args: anytype) void {
        self.log(.debug, fmt, args);
    }

    pub fn info(self: Self, comptime fmt: []const u8, args: anytype) void {
        self.log(.info, fmt, args);
    }

    pub fn warn(self: Self, comptime fmt: []const u8, args: anytype) void {
        self.log(.warn, fmt, args);
    }

    pub fn err(self: Self, comptime fmt: []const u8, args: anytype) void {
        self.log(.error, fmt, args);
    }

    pub fn setLevel(self: Self, level: Level) void {
        c.logger_set_level(self.raw, @intFromEnum(level));
    }
};
```

### Usando o Wrapper

```zig
// src/main.zig
const std = @import("std");
const clog = @import("clog.zig");

pub fn main() !void {
    // Criar logger (wrapper idiomático)
    const logger = try clog.Logger.new("MyApp", .info);
    defer logger.deinit();

    // Usar com API Zig amigável
    logger.info("Aplicação iniciada", .{});
    logger.debug("Isso não vai aparecer (nível mínimo é info)", .{});
    
    const user_count = 42;
    logger.info("Usuários conectados: {}", .{user_count});
    
    // Mudar nível dinamicamente
    logger.setLevel(.debug);
    logger.debug("Agora debug aparece!", .{});
    
    // Error com formatação
    const filename = "dados.txt";
    logger.err("Falha ao abrir arquivo: {s}", .{filename});
}
```

```zig
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "clog_demo",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Compilar código C da biblioteca
    exe.addCSourceFile(.{
        .file = b.path("vendor/clog.c"),
        .flags = &.{"-std=c99"},
    });
    
    exe.addIncludePath(b.path("vendor/"));
    exe.linkLibC();

    b.installArtifact(exe);
}
```

## Debugging Problemas Comuns

### Problema: "undefined reference"

**Causa**: Biblioteca não linkada ou símbolo não encontrado.

**Solução**:
```zig
// Verifique se adicionou:
exe.linkSystemLibrary("nome_da_lib"); // sem o 'lib' prefixo
exe.addLibraryPath(b.path("caminho/para/libs/"));
```

### Problema: "file not found" ao importar header

**Causa**: Include path não configurado.

**Solução**:
```zig
exe.addIncludePath(b.path("include/"));
// ou caminho absoluto do sistema
exe.addIncludePath(.{ .cwd_relative = "/usr/local/include" });
```

### Problema: Tipos C não reconhecidos

**Causa**: Header depende de outros headers.

**Solução**:
```zig
const c = @cImport({
    @cDefine("_GNU_SOURCE", "1");  // Defines necessários
    @cInclude("stdlib.h");         // Headers que o header depende
    @cInclude("minha_lib.h");
});
```

### Problema: Stack overflow com structs grandes

**Causa**: Struct C alocada na stack é muito grande.

**Solução**:
```zig
// ❌ Pode causar stack overflow
const big_struct: c.BigStruct = undefined;

// ✅ Alocar no heap
const big_struct = try allocator.create(c.BigStruct);
defer allocator.destroy(big_struct);
```

### Problema: Segfault ao acessar ponteiro C

**Causa**: Ponteiro nulo não verificado.

**Solução**:
```zig
const ptr = c.some_function();
if (ptr == null) {
    return error.NullPointer;
}
// Agora seguro usar ptr.?
```

## Checklist para Projetos Zig+C

Antes de iniciar um projeto misto:

- [ ] Instalar bibliotecas C de desenvolvimento (`libsqlite3-dev`, etc.)
- [ ] Configurar `build.zig` com `addIncludePath` e `linkSystemLibrary`
- [ ] Testar `@cImport` com headers simples primeiro
- [ ] Verificar conversões de tipo (especialmente strings)
- [ ] Definir estratégia de gerenciamento de memória
- [ ] Criar wrappers idiomáticos para APIs complexas
- [ ] Documentar dependências C necessárias

## Referência Rápida

### Comandos Úteis

```bash
# Verificar quais bibliotecas estão instaladas
pkg-config --list-all

# Obter flags de compilação
pkg-config --cflags --libs sqlite3

# Traduzir header
zig translate-c -I/usr/include minha_lib.h > minha_lib.zig

# Compilar C com Zig
zig cc -c arquivo.c -o arquivo.o

# Ver símbolos em uma biblioteca
nm -D libminha_lib.so
```

### Macros Comuns no build.zig

```zig
// Adicionar pkg-config automaticamente
const std = @import("std");

fn addPkgConfig(exe: *std.Build.Step.Compile, lib_name: []const u8) !void {
    // Em um script real, você usaria std.process.Child para executar pkg-config
    // e adicionar as flags automaticamente
}
```

## Próximos Passos

Agora que você domina a interoperabilidade Zig-C, explore:

1. **[Como Instalar o Zig](/tutoriais/como-instalar-zig/)** — Se ainda não configurou seu ambiente
2. **[Zig para Programadores C](/tutoriais/zig-para-programadores-c/)** — Guia completo de migração
3. **[Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)** — Allocators e estratégias de memória
4. **[Zig Build System](/tutoriais/zig-build-system/)** — Domine o build.zig

---

*Tem uma biblioteca C específica que quer usar com Zig? Entre em contato com a comunidade Zig Brasil — estamos sempre ajudando integradores!*
