@intFromPtr em Zig
O @intFromPtr converte um ponteiro para seu valor numérico de endereço de memória, representado como um usize. Esse builtin é essencial para programação de baixo nível, incluindo desenvolvimento de sistemas operacionais, drivers e alocadores de memória personalizados.
Sintaxe
@intFromPtr(ptr: anytype) usize
O que faz
O @intFromPtr extrai o endereço numérico de memória contido em um ponteiro e o retorna como um valor usize. Essa operação é o inverso de @ptrFromInt. O valor retornado representa o endereço de memória virtual onde o dado apontado reside.
Esta é uma operação de baixo nível que deve ser usada com cuidado, pois trabalhar diretamente com endereços de memória pode facilmente levar a comportamento indefinido se usado incorretamente.
Parâmetros
- ptr (
anytype): Um ponteiro de qualquer tipo. Pode ser um ponteiro simples (*T), um ponteiro para muitos ([*]T), um slice ([]T) — neste caso, retorna o endereço do início — ou qualquer outra variante de ponteiro.
Valor de retorno
Retorna um usize contendo o endereço numérico de memória do ponteiro.
Exemplos práticos
Exemplo 1: Obtendo o endereço de uma variável
const std = @import("std");
test "endereço de variável" {
var valor: u32 = 42;
const ptr = &valor;
const endereco = @intFromPtr(ptr);
std.debug.print("Valor: {}\n", .{valor});
std.debug.print("Endereço: 0x{x}\n", .{endereco});
// O endereço é um número positivo diferente de zero
try std.testing.expect(endereco != 0);
// Converter de volta para ponteiro funciona
const ptr_recuperado: *u32 = @ptrFromInt(endereco);
try std.testing.expect(ptr_recuperado.* == 42);
}
Exemplo 2: Verificação de alinhamento
const std = @import("std");
fn estaAlinhado(ptr: anytype, comptime alinhamento: usize) bool {
const endereco = @intFromPtr(ptr);
return endereco % alinhamento == 0;
}
test "verificar alinhamento" {
var buffer: [64]u8 align(16) = undefined;
const ptr = &buffer;
// Buffer declarado com align(16) deve estar alinhado a 16 bytes
try std.testing.expect(estaAlinhado(ptr, 16));
try std.testing.expect(estaAlinhado(ptr, 8));
try std.testing.expect(estaAlinhado(ptr, 4));
}
Exemplo 3: Aritmética de ponteiros para alocador personalizado
const std = @import("std");
const AlocadorBump = struct {
buffer: []u8,
offset: usize,
fn init(buffer: []u8) AlocadorBump {
return .{ .buffer = buffer, .offset = 0 };
}
fn alocar(self: *AlocadorBump, tamanho: usize, comptime alinhamento: usize) ?[*]u8 {
// Calcular endereço alinhado usando aritmética de ponteiro
const ptr_atual = @intFromPtr(self.buffer.ptr) + self.offset;
const alinhado = (ptr_atual + alinhamento - 1) & ~(@as(usize, alinhamento) - 1);
const desperdicio = alinhado - ptr_atual;
const total = desperdicio + tamanho;
if (self.offset + total > self.buffer.len) return null;
self.offset += total;
return @ptrFromInt(alinhado);
}
};
test "alocador bump" {
var buffer: [1024]u8 = undefined;
var alocador = AlocadorBump.init(&buffer);
const ptr1 = alocador.alocar(32, 8);
try std.testing.expect(ptr1 != null);
try std.testing.expect(@intFromPtr(ptr1.?) % 8 == 0);
const ptr2 = alocador.alocar(64, 16);
try std.testing.expect(ptr2 != null);
try std.testing.expect(@intFromPtr(ptr2.?) % 16 == 0);
}
Casos de uso comuns
- Alocadores de memória personalizados: Calcular alinhamentos, offsets e tamanhos de blocos alocados.
- Depuração de memória: Imprimir endereços de ponteiros para investigar layout de memória.
- Verificação de alinhamento: Verificar se um ponteiro está corretamente alinhado para operações SIMD ou requisitos de hardware.
- Desenvolvimento de kernel/drivers: Trabalhar com endereços de memória mapeados em hardware.
- Hashing de ponteiros: Usar o endereço como chave em tabelas hash.
Comparação com C equivalente
Em C, a conversão de ponteiro para inteiro é feita com cast para uintptr_t:
#include <stdint.h>
int valor = 42;
uintptr_t endereco = (uintptr_t)&valor;
Em Zig, o equivalente é:
var valor: i32 = 42;
const endereco: usize = @intFromPtr(&valor);
O tipo usize em Zig é equivalente a uintptr_t em C — ambos são garantidos ser grandes o suficiente para conter qualquer endereço de ponteiro na plataforma alvo. A diferença é que em Zig a conversão é sempre explícita com @intFromPtr, enquanto em C um cast de ponteiro para inteiro de tamanho errado (como int em vez de uintptr_t) é um erro silencioso em plataformas de 64 bits.
Segurança e restrições
@intFromPtr é uma operação segura por si só — ela apenas lê o valor numérico do ponteiro. O perigo surge ao usar esse valor para aritmética de ponteiro manual ou ao converter de volta com @ptrFromInt. Em Zig, as seguintes regras se aplicam:
- Converter um endereço de volta com
@ptrFromInte desreferenciar um ponteiro inválido causa comportamento indefinido. - O Zig não garante estabilidade de endereços entre execuções (ASLR, garbage collectors, etc.).
- Em ambientes com memória virtual, o endereço é um endereço virtual, não físico.
Para código de kernel que precisa de endereços físicos, use os mecanismos do sistema operacional ou do hardware para a tradução.
Diferença entre ponteiro e slice
Ao usar @intFromPtr em um slice, o endereço retornado é o do primeiro elemento do slice, não um ponteiro para a struct interna do slice:
var buffer: [10]u8 = undefined;
const slice = buffer[2..8];
// Endereço do slice aponta para o primeiro elemento (buffer[2])
const end_slice = @intFromPtr(slice.ptr);
const end_buffer = @intFromPtr(&buffer);
// end_slice == end_buffer + 2 (dois bytes depois do início de buffer)
Perguntas Frequentes
P: @intFromPtr funciona com ponteiros opcionais (?*T)?
Não diretamente. Ponteiros opcionais podem ser null, e @intFromPtr espera um ponteiro concreto. Desempacote o opcional primeiro: @intFromPtr(opt_ptr.?) ou verifique antes: if (opt_ptr) |p| @intFromPtr(p).
P: É seguro comparar endereços de ponteiros obtidos com @intFromPtr?
Comparar endereços de ponteiros é válido em Zig para determinar se dois ponteiros apontam para o mesmo objeto ou para determinar a ordem relativa de objetos dentro do mesmo array. Porém, comparar ponteiros de objetos não relacionados produz resultados dependentes da implementação (ordem de alocação pelo sistema).
P: Posso usar @intFromPtr para verificar se um ponteiro é nulo?
Sim, @intFromPtr(ptr) == 0 verifica se o ponteiro é nulo. No entanto, em Zig o tipo de segurança de null é gerenciado pelo sistema de tipos com ponteiros opcionais (?*T), então verificar ponteiro nulo dessa forma só é necessário ao interagir com APIs de baixo nível ou código C.
Builtins relacionados
- @ptrFromInt — Operação inversa: converte inteiro para ponteiro
- @ptrCast — Conversão entre tipos de ponteiro
- @alignCast — Converte alinhamento de ponteiro
- @alignOf — Obtém o alinhamento de um tipo
- @sizeOf — Obtém o tamanho em bytes de um tipo