@intFromPtr em Zig — Referência e Exemplos

@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

  1. Alocadores de memória personalizados: Calcular alinhamentos, offsets e tamanhos de blocos alocados.
  2. Depuração de memória: Imprimir endereços de ponteiros para investigar layout de memória.
  3. Verificação de alinhamento: Verificar se um ponteiro está corretamente alinhado para operações SIMD ou requisitos de hardware.
  4. Desenvolvimento de kernel/drivers: Trabalhar com endereços de memória mapeados em hardware.
  5. 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 @ptrFromInt e 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

Tutoriais relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.