@alignCast em Zig — Referência e Exemplos

@alignCast em Zig

O @alignCast converte um ponteiro para ter um alinhamento diferente. É necessário quando você precisa converter ponteiros entre tipos com requisitos de alinhamento distintos — por exemplo, ao converter *anyopaque (alinhamento 1) para *u32 (alinhamento 4). Em modo safe, verifica em runtime se o endereço realmente está alinhado corretamente.

Sintaxe

@alignCast(ptr: anytype) DestType

O alinhamento de destino é inferido do contexto.

Parâmetros

  • ptr (anytype): Ponteiro cujo alinhamento será convertido.

Valor de retorno

Retorna o ponteiro com o novo alinhamento. Em modo Debug/ReleaseSafe, causa panic se o endereço não estiver alinhado.

Exemplos práticos

Exemplo 1: Converter *anyopaque para tipo alinhado

const std = @import("std");

fn callback(ctx: *anyopaque) void {
    // *anyopaque tem alinhamento 1
    // *u32 requer alinhamento 4
    // @alignCast garante compatibilidade
    const ptr: *u32 = @ptrCast(@alignCast(ctx));
    std.debug.print("Valor: {}\n", .{ptr.*});
}

pub fn main() void {
    var valor: u32 = 42;
    callback(@ptrCast(&valor));
}

Exemplo 2: Combinando @ptrCast e @alignCast

const std = @import("std");

const Sensor = struct {
    id: u32,
    temperatura: f32,
};

fn processar(dados: [*]u8) void {
    // Converter bytes para ponteiro de struct com alinhamento correto
    const sensor: *Sensor = @ptrCast(@alignCast(dados));
    std.debug.print("Sensor {}: {d:.1}°C\n", .{ sensor.id, sensor.temperatura });
}

pub fn main() void {
    var sensor = Sensor{ .id = 1, .temperatura = 25.5 };
    const bytes: [*]u8 = @ptrCast(&sensor);
    processar(bytes);
}

Exemplo 3: Slice com alinhamento customizado

const std = @import("std");

fn processarAlinhado(dados: []align(16) u8) void {
    std.debug.print("Dados alinhados a 16 bytes: {} bytes\n", .{dados.len});
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    // Alocar com alinhamento específico
    const dados = try gpa.allocator().alignedAlloc(u8, 16, 64);
    defer gpa.allocator().free(dados);

    @memset(dados, 0);
    processarAlinhado(dados);
}

Casos de uso comuns

  1. Callbacks de C: Converter *anyopaque (void*) para ponteiros tipados.
  2. Serialização: Interpretar buffers de bytes como structs alinhadas.
  3. SIMD: Garantir alinhamento para instruções vetoriais.
  4. Memory-mapped I/O: Acessar registradores de hardware com alinhamento correto.

Verificação em runtime

Em modos Debug e ReleaseSafe, @alignCast insere uma verificação em runtime que causa panic se o ponteiro não estiver alinhado corretamente:

thread 0 panic: incorrect alignment

Em ReleaseFast e ReleaseSmall, a verificação é removida por performance — passar um ponteiro mal alinhado se torna undefined behavior. Por isso, @alignCast é seguro em desenvolvimento mas exige cuidado em produção.

Equivalente em C

Em C não existe verificação equivalente — cast de ponteiro sempre compila sem aviso:

void *p = malloc(1);
uint32_t *q = (uint32_t *)p; // mal alinhado, UB silencioso

Em Zig, o mesmo cenário causa panic imediato em modo debug, facilitando muito a detecção de bugs de alinhamento.

Quando @alignCast é necessário

O compilador exige @alignCast quando o alinhamento do ponteiro de destino é maior que o do ponteiro de origem. Alguns casos comuns:

DeParaNecessário?
*anyopaque (align 1)*u32 (align 4)Sim
[*]u8 (align 1)*Struct (align 8)Sim
*u32 (align 4)*u8 (align 1)Não
*u64 (align 8)*u32 (align 4)Não

Combinando com @ptrCast

Na prática, @alignCast quase sempre é usado junto com @ptrCast. A ordem correta é @ptrCast(@alignCast(ptr)):

// Correto: @alignCast ajusta o alinhamento, @ptrCast muda o tipo
const p: *MinhaStruct = @ptrCast(@alignCast(ptr_opaco));

// O compilador também aceita separados em contexto com tipo anotado:
const p2: *MinhaStruct = @alignCast(ptr_opaco);
// Zig infere o @ptrCast automaticamente quando necessário

Erros comuns

Esquecer o @alignCast ao converter de *anyopaque: O compilador emitirá um erro pedindo o cast explícito de alinhamento. Esse erro em tempo de compilação é preferível a um crash silencioso.

Passar memória não alinhada: Alocar com allocator.alloc(u8, n) e tentar fazer @alignCast para um tipo com alinhamento 16 pode falhar se o allocator não garantir esse alinhamento. Use allocator.alignedAlloc para esses casos.

Perguntas Frequentes

Por que o Zig não faz @alignCast automaticamente ao fazer @ptrCast?

Por segurança explícita. O Zig quer que o programador reconheça que está fazendo uma suposição sobre alinhamento. O @alignCast documenta essa intenção no código.

Posso usar @alignCast com slices?

Sim. @alignCast funciona com []T, [*]T, [*:0]T e outros tipos de ponteiro, não apenas *T.

Builtins relacionados

  • @ptrCast — Conversão entre tipos de ponteiro
  • @alignOf — Consultar alinhamento de um tipo
  • @constCast — Remover qualificador const
  • @as — Conversão de tipo segura

Tutoriais relacionados

Continue aprendendo Zig

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