@ptrFromInt em Zig — Referência e Exemplos

@ptrFromInt em Zig

O @ptrFromInt converte um valor inteiro (usize) para um ponteiro tipado. É a operação inversa de @intFromPtr. Este builtin é essencial para programação de sistemas, onde endereços de hardware, registradores mapeados em memória (MMIO) e tabelas de página precisam ser acessados diretamente.

Sintaxe

@ptrFromInt(comptime T: type, endereco: usize) T

Na prática, o tipo de retorno é inferido pelo contexto:

const ptr: *u32 = @ptrFromInt(endereco);

O que faz

O @ptrFromInt interpreta um valor numérico como um endereço de memória e cria um ponteiro tipado apontando para esse endereço. O compilador não verifica se o endereço é válido — essa é responsabilidade do programador.

Esta operação é considerada insegura porque não há garantia de que o endereço fornecido contenha dados válidos do tipo esperado, ou sequer que o endereço seja acessível.

Parâmetros

  • endereco (usize): O endereço de memória numérico a ser convertido para ponteiro. Deve estar alinhado conforme os requisitos do tipo de destino.

Valor de retorno

Retorna um ponteiro do tipo especificado pelo contexto, apontando para o endereço fornecido.

Exemplos práticos

Exemplo 1: Acesso a registradores de hardware (MMIO)

const std = @import("std");

// Simulação de acesso a registradores mapeados em memória
const UART_BASE: usize = 0x1000_0000;
const UART_DATA_OFFSET: usize = 0x00;
const UART_STATUS_OFFSET: usize = 0x04;

fn registradorUart(offset: usize) *volatile u32 {
    return @ptrFromInt(UART_BASE + offset);
}

fn enviarByte(byte: u8) void {
    // Escrever no registrador de dados da UART
    const reg_status = registradorUart(UART_STATUS_OFFSET);
    const reg_data = registradorUart(UART_DATA_OFFSET);

    // Esperar até que o transmissor esteja pronto
    while (reg_status.* & 0x20 != 0) {}

    // Enviar o byte
    reg_data.* = @as(u32, byte);
}

Exemplo 2: Restaurando um ponteiro previamente convertido

const std = @import("std");

fn armazenarContexto(ptr: anytype) usize {
    return @intFromPtr(ptr);
}

fn recuperarContexto(comptime T: type, endereco: usize) T {
    return @ptrFromInt(endereco);
}

test "armazenar e recuperar contexto" {
    var dados: u64 = 0xDEADBEEF;
    const endereco = armazenarContexto(&dados);

    const ptr_recuperado: *u64 = recuperarContexto(*u64, endereco);
    try std.testing.expect(ptr_recuperado.* == 0xDEADBEEF);
}

Exemplo 3: Implementação de página de memória

const std = @import("std");

const TAMANHO_PAGINA = 4096;

const Pagina = struct {
    dados: [TAMANHO_PAGINA]u8,
};

fn paginaDoEndereco(endereco: usize) *Pagina {
    // Alinhar o endereço para o início da página
    const endereco_alinhado = endereco & ~(@as(usize, TAMANHO_PAGINA - 1));
    return @ptrFromInt(endereco_alinhado);
}

fn offsetNaPagina(endereco: usize) usize {
    return endereco & (TAMANHO_PAGINA - 1);
}

test "cálculo de página" {
    const endereco: usize = 0x1234_5678;
    const offset = offsetNaPagina(endereco);
    try std.testing.expect(offset == 0x678);
}

Casos de uso comuns

  1. Registradores MMIO: Acessar periféricos de hardware mapeados em endereços fixos de memória.
  2. Desenvolvimento de sistemas operacionais: Manipular tabelas de página, IDT, GDT e outras estruturas do processador.
  3. Callbacks com contexto: Armazenar ponteiros como usize em APIs C que usam void* para dados de usuário e recuperá-los posteriormente.
  4. Alocadores de memória: Converter endereços calculados aritmeticamente de volta para ponteiros utilizáveis.
  5. Depuração: Criar ponteiros para endereços conhecidos ao investigar dumps de memória.

Builtins relacionados

Tutoriais relacionados

Continue aprendendo Zig

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