Introdução
Este guia técnico cobre a conversão de código C para Zig de forma prática, com exemplos lado a lado para cada padrão comum. Se você está planejando uma migração de projeto, leia primeiro Como Migrar um Projeto C para Zig para a estratégia de alto nível.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja Como Instalar Zig
- Conhecimento de C
- Familiaridade básica com Zig. Consulte Introdução ao Zig
Tipos Fundamentais
| Tipo C | Tipo Zig | Notas |
|---|---|---|
int | c_int ou i32 | c_int para interop, i32 para código puro Zig |
unsigned int | c_uint ou u32 | |
char | u8 | |
float | f32 | |
double | f64 | |
size_t | usize | |
ssize_t | isize | |
void* | *anyopaque | Ponteiro opaco |
NULL | null | Para optionals ?*T |
bool | bool | |
int8_t | i8 | |
uint64_t | u64 |
Variáveis e Constantes
C
const int MAX = 100;
int contador = 0;
static int privado = 42;
Zig
const MAX: i32 = 100;
var contador: i32 = 0;
const privado: i32 = 42; // visibilidade controlada por pub
Em Zig, const é imutável de verdade (não apenas “promessa ao compilador” como em C). Variáveis são var. Não existe static local — use variáveis no escopo do módulo.
Ponteiros
C
int* ptr = &valor;
int** ptr_ptr = &ptr;
void* generico = ptr;
int* array = malloc(10 * sizeof(int));
Zig
const ptr: *i32 = &valor;
const ptr_ptr: **i32 = &ptr;
const generico: *anyopaque = @ptrCast(ptr);
const array = try allocator.alloc(i32, 10);
defer allocator.free(array);
Para detalhes completos sobre conversão de ponteiros, veja Converter Ponteiros C para Zig.
Ponteiros Nullable
// C: qualquer ponteiro pode ser NULL
int* talvez_null = funcao_que_pode_retornar_null();
if (talvez_null != NULL) {
printf("%d\n", *talvez_null);
}
// Zig: nullabilidade é explícita no tipo
const talvez_null: ?*i32 = funcaoQuePodeRetornarNull();
if (talvez_null) |ptr| {
std.debug.print("{}\n", .{ptr.*});
}
Strings
C
const char* str = "Olá mundo";
char buffer[256];
snprintf(buffer, sizeof(buffer), "Valor: %d", 42);
size_t len = strlen(str);
int cmp = strcmp(str1, str2);
Zig
const str: []const u8 = "Olá mundo";
var buffer: [256]u8 = undefined;
const escrito = try std.fmt.bufPrint(&buffer, "Valor: {}", .{42});
const len = str.len;
const igual = std.mem.eql(u8, str1, str2);
Strings em Zig são slices de bytes ([]const u8) com tamanho conhecido, não null-terminated. Para converter: std.mem.span(c_string).
Structs
C
typedef struct {
double x;
double y;
} Ponto;
Ponto criar_ponto(double x, double y) {
Ponto p = {.x = x, .y = y};
return p;
}
Zig
const Ponto = struct {
x: f64,
y: f64,
pub fn criar(x: f64, y: f64) Ponto {
return .{ .x = x, .y = y };
}
};
Macros para Comptime
O preprocessador C é substituído por comptime. Veja Substituir Macros C por Comptime Zig para o guia completo.
Constantes
#define MAX_BUFFER 4096
#define PI 3.14159265358979
const MAX_BUFFER: usize = 4096;
const PI: f64 = 3.14159265358979;
Macros de Função
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
fn min(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
return if (a < b) a else b;
}
fn arraySize(comptime T: type, arr: []const T) usize {
return arr.len; // slices já carregam o tamanho
}
Compilação Condicional
#ifdef DEBUG
printf("Debug: %s\n", msg);
#endif
#if defined(__linux__)
// código Linux
#elif defined(_WIN32)
// código Windows
#endif
if (builtin.mode == .Debug) {
std.debug.print("Debug: {s}\n", .{msg});
}
if (builtin.os.tag == .linux) {
// código Linux
} else if (builtin.os.tag == .windows) {
// código Windows
}
Alocação de Memória
C
int* arr = malloc(100 * sizeof(int));
if (arr == NULL) { /* erro */ }
// usar arr...
free(arr);
void* novo = realloc(arr, 200 * sizeof(int));
Zig
const arr = try allocator.alloc(i32, 100);
defer allocator.free(arr);
// usar arr...
// Para realloc:
const novo = try allocator.realloc(arr, 200);
Veja Substituir malloc/free por Allocators Zig e ArenaAllocator.
Tratamento de Erros
C
int resultado = operacao();
if (resultado < 0) {
fprintf(stderr, "Erro: %s\n", strerror(errno));
return -1;
}
Zig
const resultado = operacao() catch |err| {
std.debug.print("Erro: {}\n", .{err});
return err;
};
// Ou com try (propaga automaticamente)
const resultado2 = try operacao();
Veja Error Sets Customizados e Padrões Try/Catch.
Loops
C
for (int i = 0; i < n; i++) {
processar(arr[i]);
}
while (condicao) {
// ...
}
do {
// ...
} while (condicao);
Zig
// Preferir for range-based
for (arr) |item| {
processar(item);
}
// Com índice
for (arr, 0..) |item, i| {
processar(item, i);
}
// While
while (condicao) {
// ...
}
// Não há do-while; usar while com break:
while (true) {
// ...
if (!condicao) break;
}
Ferramenta zig translate-c
Zig inclui uma ferramenta que traduz código C para Zig automaticamente:
zig translate-c arquivo.c > arquivo.zig
O resultado precisa de revisão manual — a tradução é literal e não idiomática. Use como ponto de partida, depois refatore para Zig idiomático.
Padrão de Migração de Arquivo
- Traduzir com
zig translate-c(ponto de partida) - Substituir tipos C por tipos Zig nativos
- Converter ponteiros para slices quando possível
- Substituir malloc/free por allocators
- Converter macros para comptime
- Adicionar error handling via error unions
- Escrever testes (
test "...") — veja Testes Unitários - Remover o arquivo
.coriginal do build
Conclusão
A migração de C para Zig é um processo incremental que pode ser feito arquivo por arquivo. A interoperabilidade transparente garante que o projeto funcione em cada etapa. O resultado é código mais seguro, mais legível e mais fácil de manter.
Para a estratégia de migração de projeto completo, veja Como Migrar um Projeto C para Zig. Para portar bibliotecas C, consulte Portar uma Biblioteca C para Zig.