@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
- Registradores MMIO: Acessar periféricos de hardware mapeados em endereços fixos de memória.
- Desenvolvimento de sistemas operacionais: Manipular tabelas de página, IDT, GDT e outras estruturas do processador.
- Callbacks com contexto: Armazenar ponteiros como
usizeem APIs C que usamvoid*para dados de usuário e recuperá-los posteriormente. - Alocadores de memória: Converter endereços calculados aritmeticamente de volta para ponteiros utilizáveis.
- Depuração: Criar ponteiros para endereços conhecidos ao investigar dumps de memória.
Builtins relacionados
- @intFromPtr — Operação inversa: converte ponteiro para inteiro
- @ptrCast — Conversão entre tipos de ponteiro
- @alignCast — Ajusta alinhamento de ponteiro
- @volatileCast — Remove qualificador volatile