Pointer Types em Zig — O que é e Como Usar
Definição
Pointer types (tipos de ponteiro) em Zig são referências a endereços de memória. Diferente de C, onde existe apenas um tipo de ponteiro (T*), Zig distingue entre vários tipos de ponteiro com semânticas diferentes. Essa distinção permite ao compilador verificar a segurança do acesso à memória e otimizar o código gerado.
Cada tipo de ponteiro carrega informações sobre: quantos elementos aponta, se é mutável ou const, qual o alinhamento, e se pode ser nulo.
Por que Pointer Types Importam
- Segurança: O sistema de tipos impede usos incorretos de ponteiros em tempo de compilação.
- Clareza: O tipo declara exatamente a intenção — um item, vários itens ou um slice com tamanho.
- Interop com C: Tipos específicos mapeiam diretamente para ponteiros C.
- Otimização: O compilador usa as informações de tipo para gerar código mais eficiente.
Tipos de Ponteiro
| Tipo | Nome | Descrição |
|---|---|---|
*T | Single-item pointer | Aponta para exatamente um valor |
*const T | Const pointer | Ponteiro somente leitura para um valor |
[*]T | Many-item pointer | Aponta para vários itens (sem tamanho conhecido) |
[]T | Slice | Ponteiro + comprimento (tamanho conhecido) |
[*:0]T | Sentinel-terminated pointer | Muitos itens terminados por sentinela |
?*T | Optional pointer | Pode ser nulo |
Exemplo Prático
Single-Item Pointer (*T)
const std = @import("std");
fn incrementar(ptr: *u32) void {
ptr.* += 1;
}
pub fn main() void {
var valor: u32 = 10;
incrementar(&valor);
std.debug.print("Valor: {}\n", .{valor}); // 11
// Ponteiro const — somente leitura
const constante: u32 = 42;
const ptr: *const u32 = &constante;
std.debug.print("Lido: {}\n", .{ptr.*}); // 42
}
Many-Item Pointer ([*]T) e Slice ([]T)
const std = @import("std");
pub fn main() void {
var array = [_]u32{ 10, 20, 30, 40, 50 };
// Slice: ponteiro + tamanho
const slice: []u32 = array[1..4]; // [20, 30, 40]
std.debug.print("Slice len: {}\n", .{slice.len}); // 3
// Many-item pointer: sem tamanho
const many: [*]u32 = &array;
std.debug.print("many[2] = {}\n", .{many[2]}); // 30
// Converter many-pointer para slice
const slice2: []u32 = many[0..5];
std.debug.print("Slice2 len: {}\n", .{slice2.len}); // 5
}
Ponteiros Opcionais (?*T)
const std = @import("std");
const Node = struct {
valor: i32,
proximo: ?*Node, // Pode ser nulo
};
fn somar_lista(inicio: ?*Node) i32 {
var soma: i32 = 0;
var atual = inicio;
while (atual) |node| {
soma += node.valor;
atual = node.proximo;
}
return soma;
}
pub fn main() void {
var c = Node{ .valor = 3, .proximo = null };
var b = Node{ .valor = 2, .proximo = &c };
var a = Node{ .valor = 1, .proximo = &b };
const total = somar_lista(&a);
std.debug.print("Soma: {}\n", .{total}); // 6
}
Coerções de Ponteiro
*[N]T --> []T (array pointer para slice)
*[N]T --> [*]T (array pointer para many-pointer)
*T --> *const T (mutável para const)
[]T --> []const T (slice mutável para const)
Armadilhas Comuns
- Dangling pointer: Retornar ponteiro para variável local causa undefined behavior. Use alocação no heap ou retorne por valor.
- Confundir
*Tcom[]T:*Taponta para um item;[]Té um slice com tamanho. Usar o tipo errado causa erros de compilação. - Many-pointer sem limites:
[*]Tnão tem.len. Não é possível iterar comforsem primeiro convertê-lo para slice. - Ponteiro nulo:
*Tnunca pode ser nulo. Use?*Tse precisar representar nulidade. - Alinhamento: Ponteiros carregam informação de alinhamento. Casts entre tipos com alinhamentos diferentes exigem
@alignCast.
Termos Relacionados
- Slice — Referência com tamanho conhecido
- Sentinel — Ponteiros e slices terminados por sentinela
- usize — Tipo usado para tamanho e indexação
- Dangling Pointer — Ponteiro para memória inválida
- Stack vs Heap — Onde os dados apontados vivem