---
title: "Zig FFI: Integração com C e C++ Tutorial Completo | Zig Brasil"
url: "https://ziglang.com.br/tutoriais/zig-ffi-integracao-cpp/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-ffi-integracao-cpp.MD"
description: "Use FFI em Zig para chamar código C/C++ e expor funções Zig. Tutorial prático com @cImport, extern e exemplos reais de interoperabilidade."
date: "2026-02-11"
author: ""
---

# Zig FFI: Integração com C e C++ Tutorial Completo | Zig Brasil

Use FFI em Zig para chamar código C/C++ e expor funções Zig. Tutorial prático com @cImport, extern e exemplos reais de interoperabilidade.


## O que é FFI e Por que Zig é Excelente para Interoperabilidade?

**Foreign Function Interface (FFI)** é o mecanismo que permite que uma linguagem de programação chame código escrito em outra linguagem. No mundo do desenvolvimento de sistemas, onde bibliotecas legadas em C e C++ dominam, uma boa FFI é essencial.

Zig se destaca particularmente nesta área porque:

1. **Compila C/C++ nativamente**: O compilador Zig pode compilar código C/C++ junto com Zig
2. **Suporte built-in a C**: Zig entende diretamente headers C e pode importá-los automaticamente
3. **ABI compatível**: Zig segue as mesmas convenções de chamada que C
4. **Sem runtime pesado**: Diferente de outras linguagens, Zig não adiciona overhead ao interagir com C

Neste guia, você aprenderá a integrar Zig com C e C++ de forma prática, desde exemplos simples até casos reais de produção.

---

## Conceitos Fundamentais de FFI

Antes de mergulharmos no código, é importante entender alguns conceitos:

### ABI (Application Binary Interface)

ABI define como funções são chamadas em nível de máquina: onde parâmetros são passados (registradores vs pilha), como valores são retornados, e como a pilha é gerenciada.

Zig usa a **ABI C** por padrão ao interagir com código externo, garantindo compatibilidade total.

### `extern` e `export`

- **`extern`**: Indica que uma função ou variável vem de fora (C ou outra linguagem)
- **`export`**: Torna uma função Zig visível para código externo

### Mangling de Nomes

C++ "mangle" (embaralha) nomes de funções para suportar sobrecarga. Para interoperar com C++ de forma compatível com C, usamos `extern "C"`.

---

## Chamando C a partir de Zig

Vamos começar com o caso mais comum: usar uma biblioteca C existente a partir do Zig.

### Exemplo 1: Usando a Biblioteca Padrão C

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

// Declara funções da libc
extern "c" fn printf(format: [*c]const u8, ...) c_int;
extern "c" fn strlen(s: [*c]const u8) usize;
extern "c" fn malloc(size: usize) ?*anyopaque;
extern "c" fn free(ptr: ?*anyopaque) void;

pub fn main() void {
    // Usando printf da libc
    _ = printf("Olá de C!\n");
    
    // Usando strlen
    const msg = "Zig é incrível";
    const len = strlen(msg);
    std.debug.print("Tamanho: {d}\n", .{len});
}
```

**Pontos importantes:**
- `[*c]const u8` é o tipo de ponteiro C para string (equivalente a `const char*`)
- O modificador `"c"` na declaração `extern` especifica a convenção de chamada C
- Tipos C são convertidos para equivalentes Zig

### Exemplo 2: Criando um Wrapper Idiomático

A forma direta acima funciona, mas não é idiomática. Vamos criar um wrapper mais "Zig-like":

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

// Declarações externas (low-level)
extern "c" fn fopen(filename: [*c]const u8, mode: [*c]const u8) ?*FILE;
extern "c" fn fclose(file: ?*FILE) c_int;
extern "c" fn fprintf(file: ?*FILE, format: [*c]const u8, ...) c_int;

const FILE = opaque {};

// Wrapper idiomático em Zig
pub const FileMode = enum {
    read,
    write,
    append,
};

pub const CFile = struct {
    handle: ?*FILE,
    
    pub fn open(path: []const u8, mode: FileMode) !CFile {
        const mode_str = switch (mode) {
            .read => "r",
            .write => "w",
            .append => "a",
        };
        
        // Converte []const u8 para [*c]const u8 (null-terminated)
        const c_path = try std.heap.c_allocator.dupeZ(u8, path);
        defer std.heap.c_allocator.free(c_path);
        
        const handle = fopen(c_path.ptr, mode_str);
        if (handle == null) return error.FileNotFound;
        
        return CFile{ .handle = handle };
    }
    
    pub fn close(self: *CFile) void {
        if (self.handle) |h| {
            _ = fclose(h);
            self.handle = null;
        }
    }
    
    pub fn write(self: CFile, msg: []const u8) void {
        if (self.handle) |h| {
            const c_msg = std.heap.c_allocator.dupeZ(u8, msg) catch return;
            defer std.heap.c_allocator.free(c_msg);
            _ = fprintf(h, "%s", c_msg.ptr);
        }
    }
};

pub fn main() !void {
    var file = try CFile.open("teste.txt", .write);
    defer file.close();
    
    file.write("Escrevendo via wrapper Zig!\n");
}
```

**Vantagens desta abstração:**
- Gerenciamento de memória automático (`defer`)
- Tipos seguros (`FileMode` enum ao invés de strings)
- Tratamento de erros Zig (`!CFile`)
- Fechamento automático com `defer`

---

## Compilando C junto com Zig

Uma das características únicas do Zig é que ele **inclui um compilador C/C++**. Isso significa que você pode compilar código C sem precisar do GCC ou Clang instalados.

### Exemplo: Biblioteca C em um Projeto Zig

**Estrutura do projeto:**
```
meu-projeto/
├── build.zig
├── src/
│   └── main.zig
└── vendor/
    └── minha-lib/
        ├── lib.c
        └── lib.h
```

**vendor/minha-lib/lib.h:**
```c
#ifndef MINHA_LIB_H
#define MINHA_LIB_H

typedef struct {
    int x;
    int y;
} Ponto;

Ponto criar_ponto(int x, int y);
int distancia(Ponto a, Ponto b);

#endif
```

**vendor/minha-lib/lib.c:**
```c
#include "lib.h"
#include <math.h>

Ponto criar_ponto(int x, int y) {
    Ponto p = {x, y};
    return p;
}

int distancia(Ponto a, Ponto b) {
    double dx = (double)(a.x - b.x);
    double dy = (double)(a.y - b.y);
    return (int)sqrt(dx * dx + dy * dy);
}
```

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

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Compila a biblioteca C
    const lib = b.addStaticLibrary(.{
        .name = "minha-lib",
        .target = target,
        .optimize = optimize,
    });
    
    lib.addCSourceFiles(.{
        .files = &.{
            "vendor/minha-lib/lib.c",
        },
        .flags = &.{
            "-std=c99",
            "-Wall",
            "-Wextra",
        },
    });
    
    lib.linkLibC();
    b.installArtifact(lib);
    
    // Executável principal
    const exe = b.addExecutable(.{
        .name = "meu-app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    exe.linkLibrary(lib);
    exe.linkLibC();
    
    // Adiciona header path para @cImport
    exe.addIncludePath(b.path("vendor/minha-lib"));
    
    b.installArtifact(exe);
    
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    
    const run_step = b.step("run", "Roda o app");
    run_step.dependOn(&run_cmd.step);
}
```

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

// Importa o header C automaticamente!
const c = @cImport({
    @cInclude("lib.h");
});

pub fn main() void {
    const p1 = c.criar_ponto(0, 0);
    const p2 = c.criar_ponto(3, 4);
    
    const dist = c.distancia(p1, p2);
    
    std.debug.print("Distância entre ({d},{d}) e ({d},{d}): {d}\n", .{
        p1.x, p1.y, p2.x, p2.y, dist
    });
}
```

**Para compilar e rodar:**
```bash
zig build run
```

**Saída:**
```
Distância entre (0,0) e (3,4): 5
```

### A Magia de `@cImport`

A função `@cImport` é extraordinária: ela **analisa headers C em tempo de compilação** e gera declarações Zig equivalentes. Isso significa:

- ✅ Sem bindings manuais
- ✅ Sem wrappers complexos
- ✅ Sempre sincronizado com o header
- ✅ Type-safe

**Limitação**: `@cImport` requer que o header C seja parseável. Headers muito complexos ou com macros complicadas podem precisar de ajustes.

---

## Exportando Funções Zig para C

Agora vamos inverter: escrever código Zig que será chamado a partir de C.

### Exemplo: Biblioteca Zig Usável por C

**src/lib.zig:**
```zig
const std = @import("std");

// Exporta função para C
export fn zig_soma(a: c_int, b: c_int) c_int {
    return a + b;
}

// Exporta com nome diferente
export fn zig_fatorial(n: c_int) c_int {
    if (n <= 1) return 1;
    return n * zig_fatorial(n - 1);
}

// Exporta estrutura
pub const PontoZig = extern struct {
    x: f64,
    y: f64,
};

export fn zig_distancia(a: PontoZig, b: PontoZig) f64 {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    return std.math.sqrt(dx * dx + dy * dy);
}

// Função que recebe callback C
export fn zig_processa_array(
    arr: [*]const c_int,
    len: usize,
    callback: ?*const fn (c_int) callconv(.C) void
) void {
    if (callback == null) return;
    
    var i: usize = 0;
    while (i < len) : (i += 1) {
        callback.?(arr[i]);
    }
}
```

**build.zig (para biblioteca):**
```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Compila como biblioteca dinâmica
    const lib = b.addSharedLibrary(.{
        .name = "ziglib",
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    b.installArtifact(lib);
}
```

**Para gerar o header C automaticamente:**

Zig pode gerar um header C compatível com suas funções exportadas:

```zig
// Adicione ao build.zig:
const generate_c_header = b.addSystemCommand(&.{
    "zig", "translate-c", 
    "--target", target.zigTriple(b.allocator) catch unreachable,
    "src/lib.zig"
});
```

Ou você pode escrever manualmente:

**ziglib.h:**
```c
#ifndef ZIGLIB_H
#define ZIGLIB_H

#ifdef __cplusplus
extern "C" {
#endif

int zig_soma(int a, int b);
int zig_fatorial(int n);

typedef struct {
    double x;
    double y;
} PontoZig;

double zig_distancia(PontoZig a, PontoZig b);

typedef void (*CallbackInt)(int);
void zig_processa_array(const int *arr, size_t len, CallbackInt callback);

#ifdef __cplusplus
}
#endif

#endif
```

**main.c (usando a biblioteca Zig):**
```c
#include <stdio.h>
#include "ziglib.h"

void imprimir(int n) {
    printf("Valor: %d\n", n);
}

int main() {
    printf("3 + 4 = %d\n", zig_soma(3, 4));
    printf("5! = %d\n", zig_fatorial(5));
    
    PontoZig p1 = {0.0, 0.0};
    PontoZig p2 = {3.0, 4.0};
    printf("Distância: %.2f\n", zig_distancia(p1, p2));
    
    int nums[] = {10, 20, 30};
    zig_processa_array(nums, 3, imprimir);
    
    return 0;
}
```

**Compilando:**
```bash
# Compila a biblioteca Zig
zig build

# Compila o programa C que usa a biblioteca
zig cc main.c -L./zig-out/lib -lziglib -o meu_app

# Roda
./meu_app
```

---

## Integração com C++

A integração com C++ é mais complexa devido ao name mangling e features como classes, templates e exceções. A estratégia recomendada é usar **C como intermediário**.

### Padrão de Interoperabilidade C++

**minha-classe.hpp (C++):**
```cpp
#ifndef MINHA_CLASSE_HPP
#define MINHA_CLASSE_HPP

class MinhaClasse {
public:
    MinhaClasse(int valor);
    ~MinhaClasse();
    
    int getValor() const;
    void setValor(int v);
    int dobrar() const;
    
private:
    int valor;
};

#endif
```

**minha-classe.cpp (C++):**
```cpp
#include "minha-classe.hpp"

MinhaClasse::MinhaClasse(int v) : valor(v) {}
MinhaClasse::~MinhaClasse() = default;

int MinhaClasse::getValor() const { return valor; }
void MinhaClasse::setValor(int v) { valor = v; }
int MinhaClasse::dobrar() const { return valor * 2; }
```

**wrapper-c.cpp (C wrapper):**
```cpp
#include "minha-classe.hpp"

extern "C" {

// Opaque pointer para esconder C++
typedef struct MinhaClasseHandle MinhaClasseHandle;

MinhaClasseHandle* minha_classe_criar(int valor) {
    return reinterpret_cast<MinhaClasseHandle*>(new MinhaClasse(valor));
}

void minha_classe_destruir(MinhaClasseHandle* handle) {
    delete reinterpret_cast<MinhaClasse*>(handle);
}

int minha_classe_get_valor(MinhaClasseHandle* handle) {
    return reinterpret_cast<MinhaClasse*>(handle)->getValor();
}

void minha_classe_set_valor(MinhaClasseHandle* handle, int valor) {
    reinterpret_cast<MinhaClasse*>(handle)->setValor(valor);
}

int minha_classe_dobrar(MinhaClasseHandle* handle) {
    return reinterpret_cast<MinhaClasse*>(handle)->dobrar();
}

} // extern "C"
```

**Usando em Zig:**
```zig
const std = @import("std");

// Declarações C das funções wrapper
extern "c" fn minha_classe_criar(valor: c_int) ?*anyopaque;
extern "c" fn minha_classe_destruir(handle: ?*anyopaque) void;
extern "c" fn minha_classe_get_valor(handle: ?*anyopaque) c_int;
extern "c" fn minha_classe_set_valor(handle: ?*anyopaque, valor: c_int) void;
extern "c" fn minha_classe_dobrar(handle: ?*anyopaque) c_int;

// Wrapper idiomático Zig
pub const MinhaClasse = struct {
    handle: ?*anyopaque,
    
    pub fn init(valor: i32) MinhaClasse {
        return .{
            .handle = minha_classe_criar(valor),
        };
    }
    
    pub fn deinit(self: *MinhaClasse) void {
        if (self.handle) |h| {
            minha_classe_destruir(h);
            self.handle = null;
        }
    }
    
    pub fn getValor(self: MinhaClasse) i32 {
        if (self.handle) |h| {
            return minha_classe_get_valor(h);
        }
        return 0;
    }
    
    pub fn setValor(self: MinhaClasse, valor: i32) void {
        if (self.handle) |h| {
            minha_classe_set_valor(h, valor);
        }
    }
    
    pub fn dobrar(self: MinhaClasse) i32 {
        if (self.handle) |h| {
            return minha_classe_dobrar(h);
        }
        return 0;
    }
};

pub fn main() void {
    var obj = MinhaClasse.init(21);
    defer obj.deinit();
    
    std.debug.print("Valor: {d}\n", .{obj.getValor()});
    std.debug.print("Dobro: {d}\n", .{obj.dobrar()});
    
    obj.setValor(50);
    std.debug.print("Novo valor: {d}\n", .{obj.getValor()});
}
```

### build.zig para C++:
```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Compila o wrapper C++
    const cxx_lib = b.addStaticLibrary(.{
        .name = "cxx-wrapper",
        .target = target,
        .optimize = optimize,
    });
    
    cxx_lib.addCSourceFiles(.{
        .files = &.{
            "src/minha-classe.cpp",
            "src/wrapper-c.cpp",
        },
        .flags = &.{
            "-std=c++17",
            "-Wall",
        },
    });
    
    cxx_lib.linkLibCpp();
    b.installArtifact(cxx_lib);
    
    // Executável Zig
    const exe = b.addExecutable(.{
        .name = "app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    exe.linkLibrary(cxx_lib);
    exe.linkLibCpp();
    
    b.installArtifact(exe);
}
```

**Por que este padrão funciona:**
- **Opaque pointer**: Esconde detalhes de C++ do Zig
- **C wrapper**: Evita name mangling e incompatibilidades ABI
- **RAII em Zig**: Usamos `defer` para destruição automática
- **Type safety**: O wrapper Zig fornece tipos seguros sobre C

---

## Gerenciamento de Memória Através de Fronteiras

Um dos maiores desafios em FFI é o gerenciamento de memória. Regras essenciais:

### Regras de Ouro

1. **Quem aloca, libera**: Se C alocou memória, C deve liberar
2. **Não misture allocators**: Use `std.heap.c_allocator` para memória que será liberada por C
3. **Cuidado com lifetimes**: Ponteiros C não têm lifetime tracking

### Exemplo Seguro: Passando Strings

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

extern "c" fn processa_string(s: [*c]const u8) void;

// ❌ ERRADO: ponteiro pode ficar inválido
pub fn enviarMensagemErrada(msg: []const u8) void {
    processa_string(msg.ptr); // msg pode não ser null-terminated!
}

// ✅ CORRETO: aloca memória C-compatível
pub fn enviarMensagemSegura(msg: []const u8) !void {
    // dupeZ aloca e adiciona null terminator
    const c_msg = try std.heap.c_allocator.dupeZ(u8, msg);
    defer std.heap.c_allocator.free(c_msg); // Zig libera
    
    processa_string(c_msg.ptr);
}

// ✅ CORRETO: C libera memória que C alocou
extern "c" fn cria_string() ?[*c]u8;
extern "c" fn libera_string(s: ?[*c]u8) void;

pub fn usarStringC() !void {
    const c_str = cria_string() orelse return error.OutOfMemory;
    defer libera_string(c_str); // C libera
    
    // Converte para slice Zig
    const len = std.mem.len(c_str);
    const slice = c_str[0..len];
    
    std.debug.print("String de C: {s}\n", .{slice});
}
```

### Usando `std.heap.c_allocator`

Este allocator usa `malloc`/`free` da libc, garantindo compatibilidade:

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

extern "c" fn recebe_buffer(buf: [*c]u8, len: usize) void;

pub fn exemploAlocacao() !void {
    // Usa o allocator compatível com C
    const allocator = std.heap.c_allocator;
    
    const buffer = try allocator.alloc(u8, 1024);
    defer allocator.free(buffer);
    
    // Passa para C
    recebe_buffer(buffer.ptr, buffer.len);
    
    // C pode manter referência temporária, mas não deve guardar
    // o ponteiro após a função retornar
}
```

---

## Caso Real: Integrando com SQLite

Vamos ver um exemplo prático e útil: usando SQLite a partir do Zig.

### Setup do Projeto

**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 = "sqlite-demo",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    // Link com SQLite
    exe.linkSystemLibrary("sqlite3");
    exe.linkLibC();
    
    b.installArtifact(exe);
}
```

**Código Zig:**
```zig
const std = @import("std");

// Importa header SQLite
const c = @cImport({
    @cInclude("sqlite3.h");
});

pub const SQLiteError = error{
    OpenError,
    ExecError,
    PrepareError,
    StepError,
};

pub const Database = struct {
    db: ?*c.sqlite3,
    
    pub fn open(path: []const u8) !Database {
        const c_path = try std.heap.c_allocator.dupeZ(u8, path);
        defer std.heap.c_allocator.free(c_path);
        
        var db: ?*c.sqlite3 = null;
        const rc = c.sqlite3_open(c_path.ptr, &db);
        
        if (rc != c.SQLITE_OK or db == null) {
            return error.OpenError;
        }
        
        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) !void {
        const c_sql = try std.heap.c_allocator.dupeZ(u8, sql);
        defer std.heap.c_allocator.free(c_sql);
        
        const rc = c.sqlite3_exec(self.db, c_sql.ptr, null, null, null);
        if (rc != c.SQLITE_OK) {
            return error.ExecError;
        }
    }
    
    pub fn query(self: Database, sql: []const u8) !ResultSet {
        const c_sql = try std.heap.c_allocator.dupeZ(u8, sql);
        defer std.heap.c_allocator.free(c_sql);
        
        var stmt: ?*c.sqlite3_stmt = null;
        const rc = c.sqlite3_prepare_v2(
            self.db,
            c_sql.ptr,
            -1, // lê até null terminator
            &stmt,
            null
        );
        
        if (rc != c.SQLITE_OK or stmt == null) {
            return error.PrepareError;
        }
        
        return ResultSet{ .stmt = stmt };
    }
};

pub const ResultSet = struct {
    stmt: ?*c.sqlite3_stmt,
    
    pub fn deinit(self: *ResultSet) void {
        if (self.stmt) |stmt| {
            _ = c.sqlite3_finalize(stmt);
            self.stmt = null;
        }
    }
    
    pub fn next(self: ResultSet) !bool {
        if (self.stmt) |stmt| {
            const rc = c.sqlite3_step(stmt);
            if (rc == c.SQLITE_ROW) return true;
            if (rc == c.SQLITE_DONE) return false;
            return error.StepError;
        }
        return false;
    }
    
    pub fn getInt(self: ResultSet, col: c_int) i32 {
        if (self.stmt) |stmt| {
            return c.sqlite3_column_int(stmt, col);
        }
        return 0;
    }
    
    pub fn getText(self: ResultSet, col: c_int) []const u8 {
        if (self.stmt) |stmt| {
            const ptr = c.sqlite3_column_text(stmt, col);
            const len = c.sqlite3_column_bytes(stmt, col);
            if (ptr == null or len == 0) return "";
            return ptr[0..@intCast(len)];
        }
        return "";
    }
};

pub fn main() !void {
    // Cria/abre banco
    var db = try Database.open("test.db");
    defer db.close();
    
    // Cria tabela
    try db.execute(
        \\CREATE TABLE IF NOT EXISTS users (
        \\    id INTEGER PRIMARY KEY,
        \\    name TEXT NOT NULL,
        \\    age INTEGER
        \\)
    );
    
    // Insere dados
    try db.execute(
        \\INSERT OR IGNORE INTO users (id, name, age) 
        \\VALUES (1, 'João', 30), (2, 'Maria', 25)
    );
    
    // Query
    var result = try db.query("SELECT id, name, age FROM users");
    defer result.deinit();
    
    std.debug.print("Usuários:\n");
    while (try result.next()) {
        const id = result.getInt(0);
        const name = result.getText(1);
        const age = result.getInt(2);
        
        std.debug.print("  {d}: {s} ({d} anos)\n", .{id, name, age});
    }
}
```

**Execução:**
```bash
# Precisa do SQLite instalado
# Ubuntu/Debian: sudo apt-get install libsqlite3-dev
# macOS: brew install sqlite3

zig build run
```

**Saída:**
```
Usuários:
  1: João (30 anos)
  2: Maria (25 anos)
```

---

## Dicas de Performance

### 1. Minimize Chamadas de Fronteira

Cada chamada entre Zig e C tem overhead. Agrupe operações:

```zig
// ❌ Ineficiente: múltiplas chamadas
extern "c" fn adiciona_item(item: Item) void;
extern "c" fn processa_lote() void;

for (items) |item| {
    adiciona_item(item); // N chamadas
}
processa_lote();

// ✅ Eficiente: uma chamada com array
extern "c" fn processa_items(items: [*]const Item, count: usize) void;

processa_items(items.ptr, items.len); // 1 chamada
```

### 2. Use `callconv(.C)` Explicitamente

Para funções de callback passadas para C:

```zig
// Sempre use callconv(.C) para callbacks
const Callback = *const fn (data: *anyopaque) callconv(.C) void;

extern "c" fn registra_callback(cb: Callback) void;
```

### 3. Alinhamento e Padding

Estruturas compartilhadas devem ter alinhamento explícito:

```zig
pub const SharedStruct = extern struct {
    // Use align para garantir compatibilidade
    campo1: u32 align(4),
    campo2: u64 align(8),
    // padding automático para alinhamento C
};
```

### 4. Evite Alocações Frequentes

```zig
// ❌ Aloca em cada iteração
for (strings) |s| {
    const c_str = try std.heap.c_allocator.dupeZ(u8, s);
    defer std.heap.c_allocator.free(c_str);
    processa(c_str);
}

// ✅ Reusa buffer
var buffer: [1024]u8 = undefined;
for (strings) |s| {
    if (s.len >= buffer.len) continue;
    
    @memcpy(buffer[0..s.len], s);
    buffer[s.len] = 0; // null terminator
    
    processa(&buffer);
}
```

---

## Depuração de Problemas FFI

### Problemas Comuns e Soluções

| Problema | Causa Provável | Solução |
|----------|---------------|---------|
| Segmentation fault | Ponteiro nulo ou inválido | Verifique null antes de usar; use `orelse` |
| Valores corrompidos | Incompatibilidade de tipos | Verifique sizes e alignments |
| Memory leak | Esqueceu `defer` ou `free` | Use `defer` sempre; track allocations |
| Link errors | Biblioteca não linkada | Adicione `exe.linkSystemLibrary("nome")` |
| Compile errors com @cImport | Header não encontrado | Adicione `exe.addIncludePath(...)` |

### Ferramentas Úteis

```bash
# Verificar símbolos em bibliotecas
nm -D libminha.so
objdump -t libminha.a

# Verificar dependências
ldd meu_executavel

# Debug com GDB
gdb ./meu_app
(gdb) break minha_funcao
(gdb) run
(gdb) bt  # backtrace
```

---

## Resumo e Próximos Passos

Neste guia, você aprendeu:

1. ✅ **Fundamentos de FFI**: ABI, extern, export, e convenções de chamada
2. ✅ **Chamar C de Zig**: Usar `@cImport`, declarar funções externas, criar wrappers
3. ✅ **Exportar Zig para C**: Compartilhar funções e estruturas com código C
4. ✅ **Integração C++**: Padrão de wrapper C para interoperar com classes C++
5. ✅ **Gerenciamento de memória**: Regras essenciais para segurança
6. ✅ **Exemplo real**: Usando SQLite do Zig

### Próximos Passos

Continue sua jornada Zig explorando estes tópicos relacionados:

1. **[Zig Build System](/tutoriais/zig-build-system/)** — Aprofunde-se em `build.zig` para projetos complexos
2. **[Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)** — Entenda allocators em profundidade
3. **[Comptime em Zig](/tutoriais/comptime-em-zig/)** — Metaprogramação poderosa
4. **[Zig para Programadores C](/tutoriais/zig-para-programadores-c/)** — Migração completa de C para Zig

### Recursos Adicionais

- [Documentação Oficial Zig - FFI](https://ziglang.org/documentation/master/#toc-Foreign-Function-Interface)
- [Zigtranslate-c](https://ziglang.org/documentation/master/#toc-cImport) — Como funciona @cImport
- [Exemplos do Zig](https://ziglang.org/learn/samples/) — Mais código de exemplo

---

*Tem dúvidas sobre FFI em Zig? Entre na discussão nos comentários ou compartilhe seu projeto!*
