---
title: "Como Migrar um Projeto C para Zig"
url: "https://ziglang.com.br/artigos/como-migrar-um-projeto-c-para-zig/"
markdown_url: "https://ziglang.com.br/artigos/como-migrar-um-projeto-c-para-zig.MD"
description: "Guia completo para migrar um projeto C existente para Zig. Estratégias de migração incremental, substituição de build system, conversão de código, interoperabilidade e armadilhas comuns."
date: "2026-02-21"
author: "Zig Brasil"
---

# Como Migrar um Projeto C para Zig

Guia completo para migrar um projeto C existente para Zig. Estratégias de migração incremental, substituição de build system, conversão de código, interoperabilidade e armadilhas comuns.


## Introdução

Migrar um projeto C existente para Zig não precisa ser um esforço de "tudo ou nada". A interoperabilidade bidirecional entre Zig e C permite uma migração incremental — arquivo por arquivo, função por função — sem nunca quebrar o build. Este artigo descreve uma estratégia prática para essa migração.

Para um guia técnico detalhado, veja [Guia de Migração: C para Zig](/tutoriais/migrar-de-c-para-zig/). Para entender as diferenças entre as linguagens, consulte [Zig vs C Moderno (C23)](/artigos/zig-vs-c-moderno/).

## Por Que Migrar?

Antes de migrar, é importante entender os benefícios concretos:

1. **Segurança**: Bounds checking, detecção de uso após liberação, null safety
2. **Manutenibilidade**: Código mais claro sem preprocessador, com error handling integrado
3. **Produtividade**: Sistema de build integrado, cross-compilation trivial, testes embutidos
4. **Performance igual ou melhor**: Mesmo backend LLVM, com otimizações extras via comptime

E os riscos:

1. **Zig é pré-1.0**: API pode mudar entre versões
2. **Curva de aprendizado**: A equipe precisa aprender Zig
3. **Ecossistema menor**: Algumas ferramentas C não têm equivalente em Zig

## Estratégia de Migração em 5 Fases

### Fase 1: Substituir o Sistema de Build

O primeiro passo é trocar Make/CMake por `build.zig`, sem alterar nenhuma linha de código C:

```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-projeto",
        .target = target,
        .optimize = optimize,
    });

    // Compilar todos os arquivos C existentes
    exe.addCSourceFiles(.{
        .files = &.{
            "src/main.c",
            "src/utils.c",
            "src/parser.c",
        },
        .flags = &.{
            "-std=c11",
            "-Wall",
            "-Wextra",
        },
    });

    exe.linkLibC();
    exe.addIncludePath(b.path("include"));

    b.installArtifact(exe);
}
```

Veja [Migrar de Makefile para build.zig](/tutoriais/migrar-makefile-para-build-zig/) e [Migrar de CMake para build.zig](/tutoriais/migrar-cmake-para-build-zig/) para guias detalhados.

### Fase 2: Adicionar Código Zig Novo

Comece escrevendo código **novo** em Zig. Novos módulos, novas funcionalidades — tudo em Zig, chamando código C existente quando necessário:

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

pub fn processarComValidacao(dados: []const u8) !Resultado {
    // Lógica nova em Zig
    if (dados.len == 0) return error.DadosVazios;

    // Chamar código C existente
    const resultado_c = c.parse(dados.ptr, dados.len);
    if (resultado_c == null) return error.ParseFalhou;

    // Pós-processamento em Zig
    return Resultado.fromC(resultado_c);
}
```

Veja [Chamar Funções C de Zig](/receitas/zig-chamar-funcao-c/) e [Converter Ponteiros C para Zig](/tutoriais/zig-converter-ponteiros-c/).

### Fase 3: Migrar Módulos Isolados

Identifique módulos C com poucas dependências e reescreva-os em Zig. Comece pelos mais simples — utilidades, parsers, algoritmos isolados:

**Antes (C):**
```c
// utils.c
#include <string.h>
#include <ctype.h>

int contar_palavras(const char* texto) {
    int count = 0;
    int em_palavra = 0;
    while (*texto) {
        if (isspace(*texto)) {
            em_palavra = 0;
        } else if (!em_palavra) {
            em_palavra = 1;
            count++;
        }
        texto++;
    }
    return count;
}
```

**Depois (Zig):**
```zig
// utils.zig
const std = @import("std");

pub fn contarPalavras(texto: []const u8) usize {
    var count: usize = 0;
    var em_palavra = false;

    for (texto) |c| {
        if (std.ascii.isWhitespace(c)) {
            em_palavra = false;
        } else if (!em_palavra) {
            em_palavra = true;
            count += 1;
        }
    }

    return count;
}

// Exportar para código C que ainda não foi migrado
export fn contar_palavras(texto: [*:0]const u8) c_int {
    const slice = std.mem.span(texto);
    return @intCast(contarPalavras(slice));
}

test "contar palavras" {
    try std.testing.expectEqual(@as(usize, 3), contarPalavras("Zig é incrível"));
    try std.testing.expectEqual(@as(usize, 0), contarPalavras(""));
}
```

### Fase 4: Substituir Padrões C por Padrões Zig

Conforme módulos são migrados, substitua padrões C por equivalentes Zig melhores:

| Padrão C | Padrão Zig |
|----------|------------|
| `malloc`/`free` | Allocators explícitos |
| Macros `#define` | `comptime` |
| `errno`/retorno de erro | Error unions |
| `NULL` checks | Optional `?T` |
| Preprocessador condicional | `comptime if` |
| `goto cleanup` | `defer`/`errdefer` |

Veja [Substituir malloc/free por Allocators](/tutoriais/zig-substituir-malloc-free/), [Substituir Macros C por Comptime](/tutoriais/zig-substituir-macros-c/) e [Padrões Errdefer](/receitas/zig-errdefer-pattern/).

### Fase 5: Remover Dependências C

Na fase final, substitua as últimas chamadas C e remova os arquivos `.c` e `.h`:

1. Substituir `#include <stdio.h>` por `std.io` e `std.fs`
2. Substituir `#include <string.h>` por `std.mem`
3. Substituir `#include <stdlib.h>` por allocators
4. Substituir bibliotecas C externas por equivalentes Zig (quando disponíveis) ou manter via `@cImport`

## Armadilhas Comuns

### 1. Tradução Literal

Não traduza C para Zig linha por linha. Aproveite os padrões idiomáticos de Zig:

```zig
// RUIM: tradução literal de C
var i: usize = 0;
while (i < array.len) : (i += 1) {
    processar(array[i]);
}

// BOM: idiomático Zig
for (array) |item| {
    processar(item);
}
```

### 2. Ignorar o Sistema de Allocators

Não use `std.heap.c_allocator` em todo lugar. Use allocators apropriados:

```zig
// Melhor: arena para operações com lifecycle definido
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
```

### 3. Tentar Migrar Tudo de Uma Vez

A migração incremental é essencial. Um projeto de 100.000 linhas de C não será migrado em uma semana. Migre módulo por módulo, mantendo o build funcional a cada passo.

## Ferramentas Úteis

- **`zig translate-c`**: Traduz automaticamente código C para Zig (resultado precisa de revisão manual)
- **`zig cc`**: Use como drop-in replacement para gcc/clang durante a transição
- **ZLS**: Language server para suporte em editores
- **`zig test`**: Testes integrados para validar cada módulo migrado

## Conclusão

A migração de C para Zig é viável e traz benefícios reais de segurança, manutenibilidade e produtividade. A chave é a abordagem incremental: comece pelo build system, adicione código novo em Zig, migre módulos isolados, e gradualmente elimine o código C.

A interoperabilidade transparente de Zig com C significa que você nunca precisa "parar tudo" para migrar. O projeto continua funcional em cada etapa.

Outra alternativa popular para migração de projetos C é <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a>, que também oferece segurança de memória e interoperabilidade com C, embora com uma abordagem diferente baseada em ownership e borrowing.

Para detalhes técnicos, consulte [Guia de Migração: C para Zig](/tutoriais/migrar-de-c-para-zig/) e [Portar uma Biblioteca C para Zig](/tutoriais/zig-portar-biblioteca-c/).
