Introdução
A conversão de ponteiros entre C e Zig é uma das tarefas mais comuns na interoperabilidade e migração. Zig tem um sistema de ponteiros mais rico que C — com distinção entre ponteiros simples, muitos-itens, nullable, e com sentinela. Este guia cobre todas as conversões necessárias.
Para interoperabilidade geral, veja Interoperabilidade C-Zig. Para chamadas de funções C, consulte Chamar Funções C de Zig.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja Como Instalar Zig
- Conhecimento de ponteiros em C
- Familiaridade com Zig. Consulte Introdução ao Zig
Tipos de Ponteiros
Mapeamento C para Zig
| C | Zig | Descrição |
|---|---|---|
int* (non-null) | *i32 | Ponteiro para um elemento |
int* (nullable) | ?*i32 | Ponteiro opcional |
int* (array) | [*]i32 | Ponteiro para muitos elementos |
const int* | [*]const i32 | Ponteiro constante para muitos |
int[] (param) | []i32 | Slice (ptr + len) |
const char* | [*:0]const u8 | String C null-terminated |
void* | *anyopaque | Ponteiro opaco |
void* (nullable) | ?*anyopaque | Ponteiro opaco nullable |
Ponteiro Simples
C
void incrementar(int* valor) {
(*valor)++;
}
int x = 41;
incrementar(&x);
// x == 42
Zig
fn incrementar(valor: *i32) void {
valor.* += 1;
}
var x: i32 = 41;
incrementar(&x);
// x == 42
O operador de dereference em Zig é .* (pós-fixo), em vez de * (pré-fixo) como em C.
Ponteiros para Arrays
C
void preencher(int* arr, size_t n) {
for (size_t i = 0; i < n; i++) {
arr[i] = (int)i;
}
}
Zig com Slice (preferido)
fn preencher(arr: []i32) void {
for (arr, 0..) |*item, i| {
item.* = @intCast(i);
}
}
Slices ([]T) são a forma idiomática em Zig — eles carregam o tamanho junto com o ponteiro, eliminando a necessidade de passar size_t n separadamente.
Zig com Many-Item Pointer (para interop C)
fn preencher(arr: [*]i32, n: usize) void {
for (0..n) |i| {
arr[i] = @intCast(i);
}
}
Converter entre os dois
// Many-item pointer + length -> Slice
const slice: []i32 = arr[0..n];
// Slice -> Many-item pointer
const ptr: [*]i32 = slice.ptr;
const len: usize = slice.len;
Strings C (null-terminated)
C
size_t comprimento(const char* str) {
return strlen(str);
}
Zig
// Receber string C null-terminated
fn comprimento(str: [*:0]const u8) usize {
return std.mem.len(str);
}
// Converter para slice Zig (sem null terminator)
fn processar(str_c: [*:0]const u8) void {
const str: []const u8 = std.mem.span(str_c);
std.debug.print("Tamanho: {} - Conteúdo: {s}\n", .{ str.len, str });
}
// Converter slice Zig para string C (para chamar funções C)
fn chamarC(allocator: std.mem.Allocator, str_zig: []const u8) !void {
const str_c = try allocator.dupeZ(u8, str_zig); // adiciona null terminator
defer allocator.free(str_c);
c.funcao_c(str_c.ptr);
}
void* (Ponteiro Opaco)
C
void* contexto = &meus_dados;
MeusDados* dados = (MeusDados*)contexto;
Zig
// Converter para anyopaque
const contexto: *anyopaque = @ptrCast(&meus_dados);
// Converter de volta
const dados: *MeusDados = @ptrCast(@alignCast(contexto));
Uso comum: callbacks com contexto
const c = @cImport(@cInclude("minha_lib.h"));
fn meuCallback(contexto: ?*anyopaque) callconv(.C) void {
if (contexto) |ctx| {
const dados: *MeusDados = @ptrCast(@alignCast(ctx));
dados.processar();
}
}
pub fn registrar(dados: *MeusDados) void {
c.registrar_callback(meuCallback, @ptrCast(dados));
}
Ponteiros Nullable
C
// Em C, QUALQUER ponteiro pode ser NULL
int* buscar(int id) {
if (id == 0) return NULL;
// ...
}
int* resultado = buscar(42);
if (resultado != NULL) {
printf("%d\n", *resultado);
}
Zig
// Em Zig, nullabilidade é explícita
fn buscar(id: i32) ?*i32 {
if (id == 0) return null;
// ...
}
if (buscar(42)) |resultado| {
std.debug.print("{}\n", .{resultado.*});
} else {
std.debug.print("Não encontrado\n", .{});
}
Aritmética de Ponteiros
C
int arr[10];
int* ptr = arr;
int* terceiro = ptr + 2;
ptrdiff_t diff = terceiro - ptr;
Zig
var arr: [10]i32 = undefined;
const ptr: [*]i32 = &arr;
const terceiro = ptr + 2;
// Aritmética de ponteiros é possível com [*]T
// Preferir slices:
const slice: []i32 = &arr;
const terceiro_valor = slice[2];
Conversão em Contexto de @cImport
Quando você usa @cImport, Zig traduz automaticamente os tipos C para tipos Zig:
const c = @cImport(@cInclude("sqlite3.h"));
pub fn exemplo() !void {
var db: ?*c.sqlite3 = null;
const rc = c.sqlite3_open("banco.db", &db);
if (rc != c.SQLITE_OK) {
if (db) |d| {
const msg = c.sqlite3_errmsg(d);
const msg_slice = std.mem.span(msg);
std.debug.print("Erro: {s}\n", .{msg_slice});
}
return error.SqliteError;
}
defer _ = c.sqlite3_close(db);
}
Tabela de Conversões Comuns
| De | Para | Método |
|---|---|---|
*T | *anyopaque | @ptrCast(ptr) |
*anyopaque | *T | @ptrCast(@alignCast(ptr)) |
[*]T | []T | ptr[0..len] |
[]T | [*]T | slice.ptr |
[*:0]u8 | []u8 | std.mem.span(ptr) |
[]u8 | [*:0]u8 | allocator.dupeZ(u8, slice) |
*T | ?*T | Implícito |
?*T | *T | opt_ptr orelse return error.NullPtr |
*[N]T | []T | ptr[0..] ou ptr (coerção) |
usize | *T | @ptrFromInt(addr) |
*T | usize | @intFromPtr(ptr) |
Conclusão
A conversão de ponteiros entre C e Zig é direta quando você entende o mapeamento de tipos. A regra geral: use slices ([]T) dentro de código Zig puro, e many-item pointers ([*]T) ou sentinel pointers ([*:0]T) na fronteira com C.
Para mais sobre interop C, veja Interoperabilidade C-Zig, Chamar Funções C de Zig e Portar uma Biblioteca C para Zig.