@volatileCast em Zig — Referência e Exemplos

@volatileCast em Zig

O @volatileCast remove o qualificador volatile de um ponteiro. Ponteiros volatile indicam que a memória apontada pode ser modificada externamente (por hardware, DMA, outra thread) e que o compilador não deve otimizar acessos a ela. Remover o volatile permite otimizações normais — use apenas quando souber que o acesso volátil não é mais necessário.

Sintaxe

@volatileCast(ptr: *volatile T) *T

Parâmetros

  • ptr (*volatile T): Ponteiro volátil a ser convertido para não-volátil.

Valor de retorno

Retorna *T — o mesmo ponteiro sem a semântica volatile.

Exemplos práticos

Exemplo 1: Registradores de hardware

const std = @import("std");

// Simulação de registradores mapeados em memória
const MMIO_BASE: usize = 0x4000_0000;

const Registrador = packed struct {
    controle: u32,
    status: u32,
    dados: u32,
};

fn lerRegistrador() void {
    // Em embarcados, registradores são volatile
    const reg: *volatile Registrador = @ptrFromInt(MMIO_BASE);

    // Ler status (acesso volatile — compilador não otimiza)
    const status = reg.status;
    _ = status;

    // Se soubermos que o valor não muda mais, podemos remover volatile
    // para permitir otimizações em cálculos subsequentes
    const reg_normal: *Registrador = @volatileCast(reg);
    _ = reg_normal;
}

Exemplo 2: Buffer compartilhado entre contextos

const std = @import("std");

var buffer_compartilhado: [256]volatile u8 = undefined;

fn copiarParaLocal() [256]u8 {
    var local: [256]u8 = undefined;
    for (0..256) |i| {
        local[i] = buffer_compartilhado[i];
    }
    return local;
}

fn processarCopia(dados: []const u8) void {
    // Agora os dados são locais e não-volatile
    // O compilador pode otimizar normalmente
    var soma: u64 = 0;
    for (dados) |byte| {
        soma += byte;
    }
    std.debug.print("Soma: {}\n", .{soma});
}

Exemplo 3: Conversão de ponteiro volatile para callback

const std = @import("std");

fn callbackNormal(ptr: *u32) void {
    std.debug.print("Valor: {}\n", .{ptr.*});
}

fn adaptarVolatile(ptr: *volatile u32) void {
    // Se soubermos que neste ponto o valor é estável
    const normal: *u32 = @volatileCast(ptr);
    callbackNormal(normal);
}

Casos de uso comuns

  1. Embarcados: Converter registradores MMIO de volatile para não-volatile após leitura.
  2. Drivers: Interfaces de hardware onde volatile é necessário apenas durante I/O.
  3. Interop: APIs que exigem ponteiros não-volatile mas os dados vêm de contexto volatile.

Regras importantes

  • Remover volatile de um ponteiro para memória que realmente muda externamente causa comportamento indefinido.
  • O compilador pode reordenar, cachear ou eliminar acessos a memória não-volatile.
  • Use @volatileCast apenas quando tiver certeza de que a semântica volatile não é mais necessária.

Builtins relacionados

Tutoriais relacionados

Continue aprendendo Zig

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