Pointer Cast Alignment — Como Resolver em Zig

Pointer Cast Alignment — Como Resolver em Zig

O Que Este Erro Significa

O erro de alinhamento em cast de ponteiros ocorre quando você tenta converter um ponteiro para um tipo que exige um alinhamento de memória mais restrito do que o ponteiro original garante. Cada tipo em Zig tem um requisito de alinhamento — por exemplo, um u32 precisa estar em um endereço múltiplo de 4. Se um ponteiro *u8 (alinhamento 1) for convertido para *u32 (alinhamento 4) e o endereço não for múltiplo de 4, ocorre um erro.

Em builds Debug, a mensagem é um panic:

thread 1 panic: incorrect alignment

Em hardware, acessar dados desalinhados pode causar crashes, resultados incorretos ou penalidades severas de desempenho, dependendo da arquitetura.

Causas Comuns

1. Cast de []u8 para Ponteiro de Tipo Maior

pub fn main() void {
    var buffer = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7 };
    // Pega um slice começando no byte 1 (endereço ímpar)
    const slice = buffer[1..5];
    // PANIC: endereço pode não ser alinhado a 4 bytes
    const ptr: *const u32 = @ptrCast(@alignCast(slice.ptr));
    _ = ptr.*;
}

2. Interpretar Bytes de Rede como Struct

const Cabecalho = struct {
    tipo: u32,    // Requer alinhamento 4
    tamanho: u32, // Requer alinhamento 4
};

pub fn main() void {
    var dados = [_]u8{ 0, 0, 0, 1, 0, 0, 0, 10, 0, 0 };
    // PANIC: dados[1..] pode não estar alinhado para Cabecalho
    const cab: *const Cabecalho = @ptrCast(@alignCast(dados[1..].ptr));
    _ = cab;
}

3. Cast de anyopaque para Tipo Específico

pub fn main() void {
    var byte: u8 = 42;
    const opaque: *anyopaque = &byte;
    // PANIC: byte tem alinhamento 1, u64 requer 8
    const ptr: *u64 = @ptrCast(@alignCast(opaque));
    _ = ptr;
}

4. Callback com Contexto Genérico

fn callback(context: *anyopaque) void {
    // PANIC: context pode não estar alinhado para u64
    const dados: *u64 = @ptrCast(@alignCast(context));
    _ = dados;
}

pub fn main() void {
    var byte: u8 = 1;
    callback(&byte);
}

5. Manipulação de Buffer de I/O

const std = @import("std");

pub fn main() void {
    var buffer: [1024]u8 = undefined;
    // Offset arbitrário pode quebrar alinhamento
    const offset: usize = 3;
    const ptr: *u32 = @ptrCast(@alignCast(&buffer[offset]));
    // PANIC se offset não é múltiplo de 4
    _ = ptr;
}

Como Corrigir

Solucao 1: Usar @alignCast com Verificação

const std = @import("std");

pub fn main() void {
    var buffer: [1024]u8 = undefined;
    const offset: usize = 4; // Múltiplo de 4

    // @alignCast verifica o alinhamento em runtime (Debug)
    const ptr: *align(1) u32 = @ptrCast(&buffer[offset]);
    _ = ptr;
}

Solucao 2: Usar std.mem.bytesAsSlice

const std = @import("std");

pub fn main() void {
    var buffer align(4) = [_]u8{ 0, 0, 0, 1, 0, 0, 0, 2 };
    // bytesAsSlice cuida do alinhamento automaticamente
    const valores = std.mem.bytesAsSlice(u32, &buffer);
    std.debug.print("primeiro: {}\n", .{valores[0]});
}

Solucao 3: Usar std.mem.readInt para Bytes Desalinhados

const std = @import("std");

pub fn main() void {
    const buffer = [_]u8{ 0, 0, 0, 1, 0, 0, 0, 2 };
    // readInt funciona com qualquer alinhamento
    const valor = std.mem.readInt(u32, buffer[1..5], .big);
    std.debug.print("valor: {}\n", .{valor});
}

Solucao 4: Declarar Buffer com Alinhamento Explícito

const std = @import("std");

pub fn main() void {
    // align(4) garante que o buffer começa em endereço alinhado a 4
    var buffer: [1024]u8 align(4) = undefined;
    // Agora é seguro ler como u32 nos offsets corretos (0, 4, 8, ...)
    const ptr: *u32 = @ptrCast(&buffer[0]);
    ptr.* = 42;
}

Solucao 5: Usar packed struct para Dados de Rede

const std = @import("std");

const Cabecalho = packed struct {
    tipo: u32,
    tamanho: u32,
};

pub fn main() void {
    const buffer = [_]u8{ 0, 0, 0, 1, 0, 0, 0, 10 };
    // packed struct não tem requisitos de alinhamento interno
    const cab: Cabecalho = @bitCast(buffer);
    std.debug.print("tipo: {}, tamanho: {}\n", .{ cab.tipo, cab.tamanho });
}

Solucao 6: Copiar para Variável Alinhada

const std = @import("std");

pub fn main() void {
    const buffer = [_]u8{ 0, 0, 0, 42 };
    // Copia os bytes para uma variável com alinhamento correto
    var valor: u32 = undefined;
    const dest: [*]u8 = @ptrCast(&valor);
    @memcpy(dest[0..4], &buffer);
    std.debug.print("valor: {}\n", .{valor});
}

Entendendo Alinhamento

Requisitos de Alinhamento Comuns

TipoAlinhamento
u8 / i81 byte
u16 / i162 bytes
u32 / i32 / f324 bytes
u64 / i64 / f648 bytes
u128 / i12816 bytes
Ponteiros4 ou 8 bytes (plataforma)

Verificar Alinhamento em Comptime

const std = @import("std");

pub fn main() void {
    comptime {
        @compileLog("u32 align:", @alignOf(u32));   // 4
        @compileLog("u64 align:", @alignOf(u64));   // 8
        @compileLog("u8 align:", @alignOf(u8));     // 1
    }
}

Erros Relacionados

Continue aprendendo Zig

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