---
title: "Portar uma Biblioteca C para Zig"
url: "https://ziglang.com.br/tutoriais/portar-uma-biblioteca-c-para-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/portar-uma-biblioteca-c-para-zig.MD"
description: "Guia completo para portar uma biblioteca C para Zig. Estratégias de wrapping, migração incremental, tradução de API, gerenciamento de memória e publicação como pacote Zig."
date: "2026-02-21"
author: "Zig Brasil"
---

# Portar uma Biblioteca C para Zig

Guia completo para portar uma biblioteca C para Zig. Estratégias de wrapping, migração incremental, tradução de API, gerenciamento de memória e publicação como pacote Zig.


## Introdução

Portar uma biblioteca C para Zig pode significar duas coisas: criar um wrapper Zig idiomático sobre a biblioteca C existente, ou reescrever a biblioteca em Zig puro. Este guia cobre ambas as abordagens e ajuda você a decidir qual é mais adequada.

Para interoperabilidade geral, veja [Interoperabilidade C-Zig](/tutoriais/zig-c-interoperabilidade/). Para conversão de ponteiros, consulte [Converter Ponteiros C para Zig](/tutoriais/zig-converter-ponteiros-c/).

## Pré-requisitos

- Zig instalado (versão 0.13+). Veja [Como Instalar Zig](/tutoriais/como-instalar-zig/)
- Código fonte da biblioteca C a ser portada
- Familiaridade com Zig e C. Consulte [Introdução ao Zig](/tutoriais/introducao-ao-zig/)

## Estratégia 1: Wrapper Zig (Recomendada para começar)

Criar um wrapper Zig sobre a biblioteca C é mais rápido e mantém a compatibilidade:

### Passo 1: Integrar a Biblioteca 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 lib = b.addStaticLibrary(.{
        .name = "minha-lib-zig",
        .root_source_file = b.path("src/wrapper.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Compilar a biblioteca C como parte do build
    lib.addCSourceFiles(.{
        .files = &.{
            "c-src/lib.c",
            "c-src/utils.c",
            "c-src/parser.c",
        },
        .flags = &.{ "-std=c11", "-Wall", "-DNDEBUG" },
    });

    lib.addIncludePath(b.path("c-src/include"));
    lib.linkLibC();

    b.installArtifact(lib);
}
```

### Passo 2: Importar e Wrappear a API

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

pub const Erro = error{
    AlocacaoFalhou,
    ArquivoNaoEncontrado,
    FormatoInvalido,
    Desconhecido,
};

fn traduzirErro(codigo: c_int) Erro {
    return switch (codigo) {
        c.ERR_ALLOC => error.AlocacaoFalhou,
        c.ERR_NOT_FOUND => error.ArquivoNaoEncontrado,
        c.ERR_FORMAT => error.FormatoInvalido,
        else => error.Desconhecido,
    };
}

pub const Parser = struct {
    handle: *c.parser_t,
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) !Parser {
        const handle = c.parser_create() orelse return error.AlocacaoFalhou;
        return .{
            .handle = handle,
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Parser) void {
        c.parser_destroy(self.handle);
    }

    pub fn parsear(self: *Parser, dados: []const u8) !Resultado {
        var resultado: c.result_t = undefined;
        const rc = c.parser_parse(self.handle, dados.ptr, dados.len, &resultado);

        if (rc != 0) return traduzirErro(rc);

        return Resultado.fromC(self.allocator, &resultado);
    }
};

pub const Resultado = struct {
    tipo: Tipo,
    valor: []const u8,
    allocator: std.mem.Allocator,

    pub fn fromC(allocator: std.mem.Allocator, c_result: *const c.result_t) !Resultado {
        const valor = std.mem.span(c_result.value);
        const copia = try allocator.dupe(u8, valor);

        return .{
            .tipo = @enumFromInt(c_result.type),
            .valor = copia,
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Resultado) void {
        self.allocator.free(self.valor);
    }

    const Tipo = enum(c_int) {
        texto = c.TYPE_TEXT,
        numero = c.TYPE_NUMBER,
        booleano = c.TYPE_BOOL,
    };
};
```

### Passo 3: Criar API Idiomática

O wrapper deve oferecer uma API que se sinta natural em Zig:

```zig
// Uso do wrapper — se sente como código Zig nativo
pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var parser = try Parser.init(allocator);
    defer parser.deinit();

    var resultado = try parser.parsear("{\"chave\": \"valor\"}");
    defer resultado.deinit();

    std.debug.print("Tipo: {} - Valor: {s}\n", .{ resultado.tipo, resultado.valor });
}

test "parser básico" {
    var parser = try Parser.init(std.testing.allocator);
    defer parser.deinit();

    var resultado = try parser.parsear("42");
    defer resultado.deinit();

    try std.testing.expectEqual(Resultado.Tipo.numero, resultado.tipo);
}
```

## Estratégia 2: Reescrita em Zig Puro

Para bibliotecas pequenas ou quando você quer eliminar a dependência de C:

### Quando Reescrever

- Biblioteca C é pequena (< 5000 linhas)
- Você quer eliminar dependências C
- A biblioteca tem bugs conhecidos que são difíceis de corrigir em C
- Você quer aproveitar features de Zig (comptime, safety checks)

### Processo

1. **Mapear a API pública**: Liste todas as funções e tipos públicos da biblioteca C
2. **Criar a estrutura de tipos em Zig**: Traduzir structs, enums, typedefs
3. **Implementar função por função**: Com testes para cada uma
4. **Manter compatibilidade C**: Exportar funções com `export` para uso por código C existente

```zig
// Exportar para consumo por código C
pub export fn parser_create() ?*Parser {
    // ...
}

pub export fn parser_destroy(p: *Parser) void {
    p.deinit();
}
```

Veja [Substituir Macros C por Comptime](/tutoriais/zig-substituir-macros-c/) e [Substituir malloc/free por Allocators](/tutoriais/zig-substituir-malloc-free/) para padrões de conversão.

## Padrões Comuns de Conversão

### Callbacks C para Zig

```c
// API C com callback
typedef void (*callback_fn)(void* ctx, const char* msg);
void registrar(callback_fn cb, void* ctx);
```

```zig
// Wrapper Zig
pub fn registrar(comptime callback: fn ([]const u8) void) void {
    const Wrapper = struct {
        fn cCallback(ctx: ?*anyopaque, msg: [*:0]const u8) callconv(.C) void {
            _ = ctx;
            const msg_slice = std.mem.span(msg);
            callback(msg_slice);
        }
    };
    c.registrar(Wrapper.cCallback, null);
}
```

### Gerenciamento de Memória na Fronteira

Quando C aloca e Zig precisa liberar (ou vice-versa), documente claramente:

```zig
/// Retorna string alocada com o allocator fornecido.
/// O chamador é responsável por liberar com allocator.free().
pub fn obterNome(allocator: std.mem.Allocator) ![]u8 {
    const c_nome = c.get_name(); // C aloca internamente
    defer c.free_name(c_nome);   // Liberar a cópia C

    // Copiar para memória gerenciada pelo allocator Zig
    return allocator.dupe(u8, std.mem.span(c_nome));
}
```

## Publicar como Pacote Zig

### build.zig.zon

```zig
.{
    .name = "minha-lib",
    .version = "1.0.0",
    .dependencies = .{},
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
        "c-src",
    },
}
```

### Expor o Módulo

```zig
// Em build.zig
const mod = b.addModule("minha-lib", .{
    .root_source_file = b.path("src/wrapper.zig"),
});

// Consumidores podem usar:
// const minha_lib = @import("minha-lib");
```

## Testes

Testes são essenciais para garantir que o wrapper se comporta corretamente:

```zig
test "wrapper produz resultados consistentes com a lib C" {
    var parser = try Parser.init(std.testing.allocator);
    defer parser.deinit();

    // Testar com dados válidos
    var resultado = try parser.parsear("dados válidos");
    defer resultado.deinit();
    try std.testing.expect(resultado.valor.len > 0);

    // Testar com dados inválidos
    const erro = parser.parsear("");
    try std.testing.expectError(error.FormatoInvalido, erro);
}
```

Veja [Testes Unitários](/receitas/zig-teste-unitario-basico/) e [Testes com Allocator](/receitas/zig-teste-com-allocator/).

## Conclusão

Portar uma biblioteca C para Zig é um processo que pode ser feito incrementalmente. Comece com um wrapper que oferece API Zig idiomática sobre a biblioteca C existente. Com o tempo, migre a implementação para Zig puro se desejável.

O resultado é uma biblioteca que se integra naturalmente com código Zig, com gerenciamento de memória explícito via allocators, error handling tipado, e testes integrados.

Para mais, veja [Chamar Funções C de Zig](/receitas/zig-chamar-funcao-c/), [Guia de Migração: C para Zig](/tutoriais/migrar-de-c-para-zig/) e [Como Migrar um Projeto C para Zig](/artigos/migrar-projeto-c-para-zig/).
