---
title: "Zig para Programadores C: Guia de Migração 2026 | Zig Brasil"
url: "https://ziglang.com.br/tutoriais/zig-para-programadores-c/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-para-programadores-c.MD"
description: "Migre de C para Zig com este guia completo. Comparação lado a lado, comptime, allocators e segurança de memória. Ideal para quem já sabe C."
date: "2026-02-24"
author: ""
---

# Zig para Programadores C: Guia de Migração 2026 | Zig Brasil

Migre de C para Zig com este guia completo. Comparação lado a lado, comptime, allocators e segurança de memória. Ideal para quem já sabe C.


Se você é um programador C experiente e está ouvindo falar cada vez mais sobre **[Zig](/tutoriais/o-que-e-zig/)**, este guia é para você. Vamos explorar, lado a lado, como os conceitos que você já domina em C se traduzem para Zig — e por que tantos desenvolvedores de sistemas estão fazendo essa transição.

## Por que Programadores C Devem Conhecer Zig?

Zig não é "mais uma linguagem moderna tentando substituir C". Diferente de Rust, que propõe um paradigma radicalmente novo com *borrow checker*, Zig foi projetada para ser uma **evolução natural de C** — mantendo o que funciona e eliminando décadas de problemas acumulados.

Aqui estão as razões principais:

- **Sem comportamento indefinido**: O pesadelo de todo programador C. Zig elimina categorias inteiras de bugs silenciosos.
- **[Interoperabilidade nativa com C](/tutoriais/zig-c-interoperabilidade/)**: Importe headers C diretamente, sem bindings manuais. Você pode migrar **gradualmente**.
- **[Build system integrado](/tutoriais/zig-build-system/)**: Esqueça Makefiles, CMake e autotools. O `build.zig` é escrito na própria linguagem.
- **Sem preprocessador**: `comptime` substitui macros com segurança de tipos e depuração real.
- **[Cross-compilation de fábrica](/tutoriais/zig-cross-compilation/)**: Compile para qualquer plataforma com um único comando, sem configurar toolchains.
- **Leitura fácil**: Se você lê C, você lê Zig. A curva de aprendizado é suave.

## Visão Geral das Diferenças: C vs Zig

Antes de mergulharmos nos detalhes, veja uma tabela comparativa das principais diferenças:

| Característica | C | Zig |
|---|---|---|
| **Preprocessador** | `#define`, `#include`, `#ifdef` | `comptime`, `@import` |
| **Ponteiros nulos** | Permitidos livremente | Tipos opcionais (`?*T`) |
| **Comportamento indefinido** | Comum e silencioso | Detectado em tempo de compilação/execução |
| **Tratamento de erros** | `errno`, códigos de retorno | Error unions (`!T`) integrados |
| **Strings** | `char*` terminadas em null | Slices (`[]const u8`) |
| **Arrays** | Decaem para ponteiros | Tipos de primeira classe, com tamanho |
| **Gerenciamento de memória** | `malloc`/`free` | Allocators explícitos e configuráveis |
| **Build system** | Make, CMake, autotools | `build.zig` integrado |
| **Generics** | Macros ou `void*` | `comptime` com segurança de tipos |
| **Módulos** | `#include` com header files | `@import` com namespaces |
| **Depuração de macros** | Praticamente impossível | `comptime` é código real, depurável |

## Variáveis e Tipos: C vs Zig

### Declaração de Variáveis

Em C, a declaração mistura tipo e nome. Em Zig, a sintaxe é mais explícita e consistente.

**C:**
```c
int x = 42;
const int y = 10;
float pi = 3.14f;
char* nome = "Zig";
unsigned long contador = 0;
```

**Zig:**
```zig
var x: i32 = 42;
const y: i32 = 10;
var pi: f32 = 3.14;
const nome: []const u8 = "Zig";
var contador: u64 = 0;
```

**Diferenças fundamentais:**

- Em Zig, **toda variável é `const` ou `var`** — não existe "mutável por padrão".
- Os tipos são explícitos: `i32` (inteiro com sinal, 32 bits), `u64` (sem sinal, 64 bits), `f32` (float 32 bits).
- Zig também suporta **inferência de tipos** quando o compilador consegue deduzir:

```zig
const x = 42; // tipo inferido como comptime_int
var nome = "Zig"; // tipo inferido como *const [3:0]u8
```

### Tipos Primitivos — Tabela de Equivalência

| C | Zig | Observação |
|---|---|---|
| `char` | `u8` | Zig não tem tipo "char" — é tudo `u8` |
| `int` | `i32` | Tamanho explícito, sem ambiguidade |
| `unsigned int` | `u32` | Nomenclatura clara e consistente |
| `long long` | `i64` | Sem confusão de tamanho por plataforma |
| `float` | `f32` | Ponto flutuante 32 bits |
| `double` | `f64` | Ponto flutuante 64 bits |
| `size_t` | `usize` | Tamanho nativo do ponteiro |
| `void*` | `*anyopaque` | Ponteiro genérico |
| `bool` (C99) | `bool` | Tipo nativo em ambos |

## Ponteiros e Gerenciamento de Memória

Esta é uma das maiores diferenças entre C e Zig. Em C, ponteiros são flexíveis mas perigosos. Zig mantém o poder dos ponteiros mas adiciona **segurança em tempo de compilação**.

### Ponteiros Básicos

**C:**
```c
int valor = 42;
int* ptr = &valor;
*ptr = 100;
printf("Valor: %d\n", *ptr);

// Ponteiro nulo — fonte de crashes
int* perigoso = NULL;
// *perigoso = 5; // Segfault!
```

**Zig:**
```zig
var valor: i32 = 42;
const ptr: *i32 = &valor;
ptr.* = 100;

// Em Zig, ponteiros NÃO podem ser nulos por padrão
// Para permitir nulo, use tipo opcional:
var talvez: ?*i32 = null;

// Acesso seguro com if
if (talvez) |p| {
    std.debug.print("Valor: {}\n", .{p.*});
} else {
    std.debug.print("Ponteiro é nulo\n", .{});
}
```

**Pontos-chave:**

- Em Zig, `*T` **nunca é nulo**. Se você precisa de nulabilidade, use `?*T` (ponteiro opcional).
- Desreferência usa `ptr.*` em vez de `*ptr` — mais legível e sem ambiguidade com multiplicação.
- Zig força você a **verificar** opcionais antes de usar, eliminando null pointer dereferences.

### Alocação de Memória

**C:**
```c
#include <stdlib.h>

// Alocação manual — fácil de esquecer o free
int* arr = (int*)malloc(100 * sizeof(int));
if (arr == NULL) {
    fprintf(stderr, "Erro de alocação\n");
    return 1;
}

arr[0] = 42;

free(arr); // Esqueceu? Memory leak!
// arr[0] = 10; // Use-after-free! Comportamento indefinido.
```

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

pub fn main() !void {
    // Allocator é explícito — você escolhe a estratégia
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit(); // Detecta leaks ao sair!
    const allocator = gpa.allocator();

    // Alocação com tratamento de erro integrado
    const arr = try allocator.alloc(i32, 100);
    defer allocator.free(arr); // Garantia de liberação com defer

    arr[0] = 42;
}
```

**Vantagens do modelo Zig:**

- **[Allocators explícitos](/tutoriais/gerenciamento-de-memoria-zig/)**: Você sabe exatamente quem aloca e libera memória. Pode trocar a estratégia (arena, page, etc.) sem alterar a lógica.
- **`defer`**: Garante que `free` é chamado quando o escopo termina — impossível esquecer.
- **Detecção de leaks**: O `GeneralPurposeAllocator` detecta memory leaks em modo debug.

## Arrays e Slices

Em C, arrays e ponteiros são intimamente ligados (e confusos). Zig separa claramente os conceitos.

### Arrays

**C:**
```c
int arr[5] = {1, 2, 3, 4, 5};
int tamanho = sizeof(arr) / sizeof(arr[0]); // Hack clássico

// Array decai para ponteiro ao passar para função
void processar(int* arr, int tamanho) {
    // Perdemos a informação de tamanho!
    for (int i = 0; i < tamanho; i++) {
        printf("%d ", arr[i]);
    }
}

processar(arr, 5);
```

**Zig:**
```zig
const arr = [_]i32{ 1, 2, 3, 4, 5 };
const tamanho = arr.len; // Tamanho sempre disponível!

// Zig preserva o tipo completo
fn processar(dados: []const i32) void {
    for (dados) |valor| {
        std.debug.print("{} ", .{valor});
    }
}

processar(&arr); // Coerção automática de *[5]i32 para []const i32
```

### Slices — O Conceito que Falta em C

Slices são uma das features mais poderosas de Zig que **não existem em C**. Um slice é um par (ponteiro, tamanho) que referencia uma porção de um array.

**C (simulando slices manualmente):**
```c
// Em C, você faz isso na mão
void processar_slice(int* dados, int inicio, int fim) {
    for (int i = inicio; i < fim; i++) {
        printf("%d ", dados[i]);
    }
}
```

**Zig (slices nativos):**
```zig
const arr = [_]i32{ 10, 20, 30, 40, 50 };

// Criando um slice — simples e seguro
const slice = arr[1..4]; // [20, 30, 40]
std.debug.print("Tamanho: {}\n", .{slice.len}); // 3

// Bounds checking automático em modo debug
// slice[10]; // Erro em tempo de execução: index out of bounds
```

Slices eliminam buffer overflows carregando sempre a informação de tamanho junto com o ponteiro.

## Tratamento de Erros

O [tratamento de erros](/tutoriais/tratamento-de-erros-em-zig/) é onde Zig realmente brilha em comparação com C. Chega de `errno`, códigos de retorno mágicos e erros silenciosamente ignorados.

### A Abordagem C

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

FILE* abrir_arquivo(const char* caminho) {
    FILE* f = fopen(caminho, "r");
    if (f == NULL) {
        // errno é global e thread-unsafe (em algumas implementações)
        perror("Erro ao abrir arquivo");
        return NULL;
    }
    return f;
}

int main() {
    FILE* f = abrir_arquivo("dados.txt");
    if (f == NULL) {
        return 1; // Fácil esquecer essa verificação
    }
    // ... usar arquivo
    fclose(f);
    return 0;
}
```

**Problemas do modelo C:**
- Nada obriga verificar o retorno de `fopen`.
- `errno` é uma variável global — pode ser sobrescrita.
- Convenções de erro variam: retorno `-1`, `NULL`, `errno`... cada API faz diferente.

### A Abordagem Zig

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

fn abrirArquivo(caminho: []const u8) !std.fs.File {
    // O '!' no retorno indica que a função pode falhar
    return std.fs.cwd().openFile(caminho, .{});
}

pub fn main() !void {
    // 'try' propaga o erro automaticamente
    const arquivo = try abrirArquivo("dados.txt");
    defer arquivo.close(); // Garante fechamento

    // Ou trate o erro explicitamente:
    const arquivo2 = abrirArquivo("outro.txt") catch |err| {
        std.debug.print("Erro: {}\n", .{err});
        return;
    };
    _ = arquivo2;
}
```

### Error Unions Explicados

Em Zig, o tipo `!T` (chamado *error union*) significa "retorna `T` ou retorna um erro". O compilador **obriga** você a tratar o erro. Não existe como ignorar silenciosamente.

```zig
// Definindo seus próprios erros
const MeuErro = error{
    ArquivoNaoEncontrado,
    SemPermissao,
    DadosInvalidos,
};

fn processar(dados: []const u8) MeuErro!u32 {
    if (dados.len == 0) return MeuErro.DadosInvalidos;
    // ... processar dados
    return 42;
}

// Uso com switch para tratar cada caso
const resultado = processar(buffer) catch |err| switch (err) {
    MeuErro.ArquivoNaoEncontrado => {
        std.debug.print("Arquivo não encontrado\n", .{});
        return;
    },
    MeuErro.SemPermissao => {
        std.debug.print("Sem permissão\n", .{});
        return;
    },
    MeuErro.DadosInvalidos => {
        std.debug.print("Dados inválidos\n", .{});
        return;
    },
};
```

**Resumo da comparação:**

| Aspecto | C | Zig |
|---|---|---|
| Mecanismo | `errno`, códigos de retorno | Error unions (`!T`) |
| Obrigatório tratar? | Não | Sim (verificado pelo compilador) |
| Propagação | Manual (if/return) | `try` (uma palavra-chave) |
| Definição de erros | Constantes `#define` | `error` sets tipados |

## Comptime vs Preprocessador C

O preprocessador C (`#define`, `#ifdef`, `#include`) é essencialmente um **sistema de substituição de texto** que roda antes da compilação. Zig substitui isso com [`comptime`](/tutoriais/comptime-em-zig/) — **código real executado em tempo de compilação**.

### Macros C vs Comptime Zig

**C — Macro para valor máximo:**
```c
// Problemas clássicos de macros:
// 1. Sem verificação de tipos
// 2. Efeitos colaterais (avaliação dupla)
// 3. Impossível depurar

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int resultado = MAX(x++, y++);
// Bug! x++ ou y++ é avaliado DUAS vezes!
```

**Zig — Função genérica com comptime:**
```zig
fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

// Uso: seguro, tipado, depurável
const resultado = max(i32, x, y);
// Sem efeitos colaterais. Sem avaliação dupla.
// Erro de compilação se os tipos não suportarem '>'
```

### Compilação Condicional

**C:**
```c
#ifdef DEBUG
    printf("Debug: valor = %d\n", x);
#endif

#ifdef _WIN32
    // Código Windows
#elif __linux__
    // Código Linux
#else
    // Outro SO
#endif
```

**Zig:**
```zig
const builtin = @import("builtin");

// Compilação condicional com código real
if (builtin.mode == .Debug) {
    std.debug.print("Debug: valor = {}\n", .{x});
}

// Detecção de plataforma
if (builtin.os.tag == .windows) {
    // Código Windows
} else if (builtin.os.tag == .linux) {
    // Código Linux
} else {
    // Outro SO
}
```

### Geração de Código em Comptime

Uma das capacidades mais poderosas de `comptime` é gerar código complexo em tempo de compilação, algo impossível com macros C:

```zig
// Gerar uma lookup table em tempo de compilação
fn gerarTabela() [256]u8 {
    var tabela: [256]u8 = undefined;
    for (0..256) |i| {
        tabela[i] = @intCast((i * i) % 256);
    }
    return tabela;
}

// A tabela é calculada em tempo de COMPILAÇÃO
// Zero custo em tempo de execução!
const TABELA = comptime gerarTabela();

pub fn main() void {
    // Acesso instantâneo — é como se fosse uma constante literal
    std.debug.print("tabela[10] = {}\n", .{TABELA[10]});
}
```

Em C, para fazer algo similar, você precisaria de scripts externos gerando código ou macros extremamente complexas e ilegíveis.

## Build System: Zig Build vs Make/CMake

Se você já escreveu Makefiles ou lutou com CMake, o sistema de build do Zig vai parecer um sonho.

### Makefile Típico em C

```makefile
CC = gcc
CFLAGS = -Wall -Wextra -O2
LDFLAGS = -lm -lpthread

SRCS = main.c utils.c parser.c
OBJS = $(SRCS:.c=.o)
TARGET = meu_programa

$(TARGET): $(OBJS)
	$(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)

.PHONY: clean
```

### build.zig Equivalente

```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,
    });

    // Linkar com bibliotecas C
    exe.linkSystemLibrary("m");       // libm
    exe.linkSystemLibrary("pthread"); // libpthread
    exe.linkLibC();

    b.installArtifact(exe);

    // Comando para rodar
    const run_cmd = b.addRunArtifact(exe);
    const run_step = b.step("run", "Executa o programa");
    run_step.dependOn(&run_cmd.step);
}
```

**Vantagens do `build.zig`:**

| Aspecto | Make/CMake | Zig Build |
|---|---|---|
| **Linguagem** | DSL própria (Make) ou CMake script | Zig (a mesma linguagem do projeto!) |
| **Cross-compilation** | Configuração complexa | `zig build -Dtarget=aarch64-linux` |
| **Dependências** | pkg-config, find_package | `zig fetch` + `build.zig.zon` |
| **Depuração** | Difícil | Depurável como código normal |
| **Portabilidade** | Shell-dependente (Make) | Funciona igual em todos os SOs |

Para compilar e rodar:

```bash
# Zig — um único comando
zig build run

# Equivalente em C com Make
make && ./meu_programa
```

Para cross-compilar para ARM Linux (por exemplo, Raspberry Pi):

```bash
# Zig — simples assim
zig build -Dtarget=aarch64-linux-gnu

# C — boa sorte configurando o toolchain... 😅
```

## Interoperabilidade com C: Importando Headers

Uma das features matadoras de Zig é a capacidade de **importar headers C diretamente** e usar bibliotecas C sem escrever bindings manuais.

### Importando a libc

**C:**
```c
#include <stdio.h>
#include <string.h>
#include <math.h>

int main() {
    printf("Raiz de 2: %f\n", sqrt(2.0));
    return 0;
}
```

**Zig usando a libc:**
```zig
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("math.h");
});

pub fn main() void {
    _ = c.printf("Raiz de 2: %f\n", c.sqrt(2.0));
}
```

### Usando uma Biblioteca C Existente

Imagine que você tem uma biblioteca C chamada `libconfig.h`:

```c
// libconfig.h
typedef struct {
    char* nome;
    int valor;
} Config;

Config* config_criar(const char* nome, int valor);
void config_destruir(Config* cfg);
int config_obter_valor(const Config* cfg);
```

**Usando em Zig:**
```zig
const c = @cImport({
    @cInclude("libconfig.h");
});

pub fn main() !void {
    // Chamar funções C diretamente
    const cfg = c.config_criar("teste", 42) orelse {
        @panic("Falha ao criar config");
    };
    defer c.config_destruir(cfg);

    const valor = c.config_obter_valor(cfg);
    std.debug.print("Valor: {}\n", .{valor});
}
```

No `build.zig`:
```zig
exe.addIncludePath(b.path("include/"));
exe.linkSystemLibrary("config");
exe.linkLibC();
```

### Migração Gradual

A interoperabilidade bidirecional permite migrar **um arquivo por vez**:

1. Comece com um projeto 100% C.
2. Adicione um `build.zig` e compile o código C existente com `zig cc`.
3. Reescreva um módulo em Zig, mantendo os headers C como interface.
4. Os módulos C e Zig coexistem no mesmo binário.
5. Gradualmente, substitua cada módulo C por Zig.

Essa abordagem é **muito mais prática** do que reescrever tudo do zero.

## Exemplo Prático: Reescrevendo um Programa C em Zig

Vamos reescrever um programa C real — um contador de palavras em um arquivo — mostrando como cada parte se traduz para Zig.

### Versão C

```c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

typedef struct {
    int linhas;
    int palavras;
    int caracteres;
} Contagem;

Contagem contar(const char* caminho) {
    Contagem result = {0, 0, 0};
    FILE* f = fopen(caminho, "r");
    if (!f) {
        fprintf(stderr, "Erro ao abrir: %s\n", caminho);
        result.linhas = -1; // Sinalizar erro com valor mágico
        return result;
    }

    int c;
    int em_palavra = 0;
    while ((c = fgetc(f)) != EOF) {
        result.caracteres++;
        if (c == '\n') result.linhas++;
        if (isspace(c)) {
            em_palavra = 0;
        } else if (!em_palavra) {
            em_palavra = 1;
            result.palavras++;
        }
    }

    fclose(f);
    return result;
}

int main(int argc, char** argv) {
    if (argc != 2) {
        fprintf(stderr, "Uso: %s <arquivo>\n", argv[0]);
        return 1;
    }

    Contagem c = contar(argv[1]);
    if (c.linhas == -1) return 1;

    printf("Linhas:     %d\n", c.linhas);
    printf("Palavras:   %d\n", c.palavras);
    printf("Caracteres: %d\n", c.caracteres);
    return 0;
}
```

### Versão Zig

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

const Contagem = struct {
    linhas: u32 = 0,
    palavras: u32 = 0,
    caracteres: u32 = 0,
};

const ContagemErro = error{
    ArquivoNaoEncontrado,
    SemPermissao,
    ErroLeitura,
};

fn contar(caminho: []const u8) !Contagem {
    // Abrir arquivo — erros são tratados automaticamente com 'try'
    const arquivo = std.fs.cwd().openFile(caminho, .{}) catch |err| {
        std.debug.print("Erro ao abrir '{s}': {}\n", .{ caminho, err });
        return err;
    };
    defer arquivo.close(); // Fecha automaticamente ao sair do escopo

    var result = Contagem{};
    var em_palavra = false;

    // Leitura buffered — eficiente e idiomática
    var buf_reader = std.io.bufferedReader(arquivo.reader());
    const reader = buf_reader.reader();

    while (true) {
        const byte = reader.readByte() catch |err| switch (err) {
            error.EndOfStream => break,
            else => return err,
        };

        result.caracteres += 1;

        if (byte == '\n') result.linhas += 1;

        if (std.ascii.isWhitespace(byte)) {
            em_palavra = false;
        } else if (!em_palavra) {
            em_palavra = true;
            result.palavras += 1;
        }
    }

    return result;
}

pub fn main() !void {
    // Argumentos de linha de comando — sem ponteiros crus
    const args = try std.process.argsAlloc(std.heap.page_allocator);
    defer std.process.argsFree(std.heap.page_allocator, args);

    if (args.len != 2) {
        std.debug.print("Uso: {s} <arquivo>\n", .{args[0]});
        std.process.exit(1);
    }

    const result = try contar(args[1]);

    const stdout = std.io.getStdOut().writer();
    try stdout.print("Linhas:     {}\n", .{result.linhas});
    try stdout.print("Palavras:   {}\n", .{result.palavras});
    try stdout.print("Caracteres: {}\n", .{result.caracteres});
}
```

### O que Melhorou?

| Aspecto | Versão C | Versão Zig |
|---|---|---|
| **Erros** | Valor mágico `-1` | Error union tipado |
| **Memória** | `fclose` pode ser esquecido | `defer` garante fechamento |
| **Segurança** | Buffer overflow possível | Bounds checking automático |
| **Strings** | `char*` sem tamanho | `[]const u8` com tamanho |
| **Argumentos** | `char** argv` cru | API tipada com alocação explícita |

## Chamando Zig a partir de C

A interoperabilidade em Zig e bidirecional. Alem de chamar C a partir de Zig, voce pode **exportar funcoes Zig** para serem consumidas por codigo C existente. Isso e fundamental para migracao gradual.

### Exportando Funcoes Zig para C

Crie um arquivo Zig que exporta funcoes com a convencao de chamada C:

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

// export torna a funcao visivel para C
// callconv(.C) usa a convencao de chamada C
export fn somar(a: i32, b: i32) i32 {
    return a + b;
}

export fn fatorial(n: u32) u64 {
    if (n <= 1) return 1;
    var resultado: u64 = 1;
    var i: u32 = 2;
    while (i <= n) : (i += 1) {
        resultado *= @intCast(i);
    }
    return resultado;
}

// Funcoes que usam allocators podem ser exportadas
// mas precisam gerenciar memoria de forma compativel com C
export fn criar_buffer(tamanho: usize) ?[*]u8 {
    const buf = std.heap.page_allocator.alloc(u8, tamanho) catch return null;
    return buf.ptr;
}

export fn liberar_buffer(ptr: [*]u8, tamanho: usize) void {
    const slice = ptr[0..tamanho];
    std.heap.page_allocator.free(slice);
}
```

### Usando no Codigo C

```c
// main.c
#include <stdio.h>
#include <stdint.h>

// Declaracoes das funcoes exportadas pelo Zig
extern int32_t somar(int32_t a, int32_t b);
extern uint64_t fatorial(uint32_t n);
extern uint8_t* criar_buffer(size_t tamanho);
extern void liberar_buffer(uint8_t* ptr, size_t tamanho);

int main() {
    // Chamar funcoes Zig normalmente
    printf("3 + 4 = %d\n", somar(3, 4));
    printf("10! = %lu\n", fatorial(10));

    // Gerenciar memoria alocada pelo Zig
    uint8_t* buf = criar_buffer(1024);
    if (buf) {
        buf[0] = 42;
        printf("buf[0] = %d\n", buf[0]);
        liberar_buffer(buf, 1024);
    }

    return 0;
}
```

### Compilando o Projeto Misto

No `build.zig`, voce pode compilar arquivos C e Zig juntos:

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

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

    // Biblioteca Zig compilada como biblioteca estatica
    const lib = b.addStaticLibrary(.{
        .name = "mathlib",
        .root_source_file = b.path("src/mathlib.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Executavel C que usa a biblioteca Zig
    const exe = b.addExecutable(.{
        .name = "programa_c",
        .target = target,
        .optimize = optimize,
    });
    exe.addCSourceFile(.{ .file = b.path("src/main.c") });
    exe.linkLibrary(lib);
    exe.linkLibC();

    b.installArtifact(exe);
}
```

Essa capacidade bidirecional significa que voce pode introduzir Zig em qualquer projeto C existente **sem reescrever nada** -- basta adicionar novos modulos em Zig e exportar as funcoes necessarias.

## Quando Usar Zig vs Quando Ficar com C

Zig não é a resposta para **tudo**. Veja quando cada linguagem faz mais sentido:

### ✅ Use Zig quando:

- **Projetos novos** de sistemas/infraestrutura — aproveite todas as melhorias desde o início.
- **Cross-compilation** é necessária — Zig torna trivial o que em C requer horas de setup.
- **Segurança importa** — menos comportamento indefinido, menos bugs de memória.
- **A equipe pode aprender** — a curva de aprendizado para quem já sabe C é suave.
- **Você quer substituir C em um projeto existente gradualmente** — a interop nativa facilita.
- **Substituir macros complexas** — `comptime` é infinitamente mais poderoso e seguro.

### ✅ Fique com C quando:

- **Código legado massivo** — reescrever milhões de linhas não é prático.
- **Ecossistema específico** — drivers de kernel Linux, por exemplo, ainda exigem C.
- **Zig ainda não atingiu 1.0** — para produção crítica em que estabilidade da linguagem é essencial.
- **A equipe não tem capacidade** de aprender uma linguagem nova no momento.
- **Compiladores certificados** são exigidos (aeroespacial, automotivo) — C tem compilers certificados há décadas.

### A Melhor Estratégia: Migração Gradual

Graças à interoperabilidade nativa, você não precisa escolher entre C **ou** Zig. Você pode:

1. Usar `zig cc` como compilador C para ganhar cross-compilation.
2. Escrever novos módulos em Zig que convivem com o código C existente.
3. Gradualmente substituir módulos C por Zig, testando cada etapa.

## Próximos Passos

Pronto para começar sua jornada com Zig? Aqui estão os recursos recomendados:

1. **[Instale o Zig](/tutoriais/como-instalar-zig/)** -- siga nosso guia de instalacao para Linux, macOS e Windows.
2. **[Entenda o Build System](/tutoriais/zig-build-system/)** -- aprenda como substituir Makefiles e CMake pelo `build.zig`.
3. **[Gerenciamento de Memoria em Zig](/tutoriais/gerenciamento-de-memoria-zig/)** -- domine allocators, arena allocators e estrategias de memoria.
4. **[Comptime em Profundidade](/tutoriais/comptime-em-zig/)** -- explore o poder da metaprogramacao em tempo de compilacao.
5. **[Tratamento de Erros em Zig](/tutoriais/tratamento-de-erros-em-zig/)** -- entenda error unions, `try`, `catch` e error sets.
6. **[Structs, Enums e Unions](/tutoriais/structs-enums-unions-zig/)** -- tipos compostos em Zig comparados com C.
7. **[Testes em Zig](/tutoriais/testes-zig/)** -- descubra como o sistema de testes integrado supera qualquer framework C.
8. **[Zig vs C em Detalhe](/tutoriais/zig-vs-c-comparacao/)** -- uma comparacao aprofundada entre as duas linguagens.
9. **[Zig para Programadores Rust](/tutoriais/zig-para-programadores-rust/)** -- se voce tambem conhece Rust, veja como Zig se compara.
10. **[Participe da Comunidade](/comunidade/)** -- junte-se ao Discord do Zig e ao grupo Zig Brasil.

---

*Este guia e parte da serie de tutoriais do ZigLang Brasil. Se voce achou util, compartilhe com outros programadores C que podem se beneficiar de conhecer Zig!*
