Alignment Error — Como Resolver em Zig

Alignment Error — Como Resolver em Zig

O Que Este Erro Significa

Erros de alinhamento ocorrem quando dados são colocados em endereços de memória que não atendem aos requisitos de alinhamento do tipo. Cada tipo em Zig tem um requisito de alinhamento natural — por exemplo, u32 precisa estar em endereço múltiplo de 4, u64 em múltiplo de 8. Acessar dados desalinhados pode causar panic em builds Debug ou, em certas arquiteturas, crashes de hardware.

A mensagem de panic:

thread 1 panic: incorrect alignment

Alinhamento é um conceito de hardware: processadores acessam memória de forma mais eficiente (ou, em algumas arquiteturas, apenas) quando os dados estão em endereços que são múltiplos do tamanho do tipo.

Causas Comuns

1. Cast de Ponteiro com Alinhamento Incompatível

pub fn main() void {
    var bytes = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7 };
    // Ponteiro no byte 1 — endereço ímpar, não alinhado a 4
    const ptr: *u32 = @ptrCast(@alignCast(&bytes[1]));
    // PANIC: endereço não é múltiplo de 4
    _ = ptr.*;
}

2. Alocação Personalizada sem Considerar Alinhamento

const std = @import("std");

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

    const bytes = try allocator.alloc(u8, 100);
    defer allocator.free(bytes);

    // bytes pode não estar alinhado para u64
    const ptr: *u64 = @ptrCast(@alignCast(bytes.ptr));
    // PANIC se bytes.ptr não é múltiplo de 8
    _ = ptr;
}

3. Struct com Campo que Requer Alinhamento Especial

const std = @import("std");

const DadosSIMD = struct {
    vetor: @Vector(4, f32) align(16), // Requer alinhamento de 16 bytes
};

pub fn main() void {
    // Se a struct não estiver em endereço alinhado a 16...
    var dados: DadosSIMD = undefined;
    dados.vetor = @splat(1.0);
    _ = dados;
}

4. Interpretar Buffer de Rede como Struct

pub fn main() void {
    var buffer: [100]u8 = undefined;
    // Dados de rede começam no byte 3 (offset arbitrário)
    const offset: usize = 3;
    const ptr: *u32 = @ptrCast(@alignCast(&buffer[offset]));
    // PANIC: offset 3 não é alinhado a 4
    _ = ptr;
}

5. Alocação de Tipo com Alinhamento Não-Padrão

const std = @import("std");

const AlinhamentoEspecial = struct {
    dados: [64]u8 align(64), // Cache line aligned
};

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

    // alloc padrão pode não fornecer alinhamento de 64
    const ptr = try allocator.create(AlinhamentoEspecial);
    defer allocator.destroy(ptr);
    _ = ptr;
}

Como Corrigir

Solucao 1: Declarar Buffer com align Explícito

pub fn main() void {
    // align(4) garante que buffer começa em endereço alinhado a 4
    var buffer: [100]u8 align(4) = undefined;

    // Agora é seguro ler como u32 em offsets múltiplos de 4
    const ptr: *u32 = @ptrCast(&buffer[0]);
    ptr.* = 42;

    const ptr2: *u32 = @ptrCast(&buffer[4]);
    ptr2.* = 100;
}

Solucao 2: Usar std.mem.readInt e writeInt

Para ler/escrever inteiros de buffers arbitrários sem requisitos de alinhamento:

const std = @import("std");

pub fn main() void {
    var buffer = [_]u8{ 0, 0, 0, 42, 0, 0, 1, 0 };

    // readInt funciona com QUALQUER alinhamento
    const valor1 = std.mem.readInt(u32, buffer[0..4], .little);
    const valor2 = std.mem.readInt(u32, buffer[1..5], .little); // Offset ímpar — OK!
    std.debug.print("valor1: {}, valor2: {}\n", .{ valor1, valor2 });

    // writeInt também
    std.mem.writeInt(u32, buffer[3..7], 0x12345678, .big);
}

Solucao 3: Usar @alignCast com Verificação

const std = @import("std");

fn processarComoU32(bytes: []u8) void {
    // Verificar alinhamento antes
    if (@intFromPtr(bytes.ptr) % @alignOf(u32) != 0) {
        std.debug.print("Buffer não está alinhado para u32\n", .{});
        return;
    }

    const ptr: *u32 = @ptrCast(@alignCast(bytes.ptr));
    std.debug.print("Valor: {}\n", .{ptr.*});
}

Solucao 4: Copiar para Variável Alinhada

const std = @import("std");

pub fn main() void {
    const buffer = [_]u8{ 0x78, 0x56, 0x34, 0x12 };

    // Copiar bytes para variável com alinhamento correto
    var valor: u32 = undefined;
    @memcpy(std.mem.asBytes(&valor), &buffer);
    std.debug.print("valor: 0x{X}\n", .{valor});
}

Solucao 5: Usar std.mem.bytesAsValue

const std = @import("std");

const Cabecalho = extern struct {
    tipo: u16,
    tamanho: u16,
};

pub fn main() void {
    var buffer align(@alignOf(Cabecalho)) = [_]u8{ 0x01, 0x00, 0x10, 0x00 };
    // bytesAsValue com buffer alinhado
    const cab = std.mem.bytesAsValue(Cabecalho, &buffer);
    std.debug.print("tipo: {}, tamanho: {}\n", .{ cab.tipo, cab.tamanho });
}

Solucao 6: Alocar com Alinhamento Específico

const std = @import("std");

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

    // allocAdvanced com alinhamento específico
    const buffer = try allocator.alignedAlloc(u8, 64, 1024);
    defer allocator.free(buffer);

    // buffer está alinhado a 64 bytes
    std.debug.print("Endereço: 0x{X} (alinhado a 64: {})\n", .{
        @intFromPtr(buffer.ptr),
        @intFromPtr(buffer.ptr) % 64 == 0,
    });
}

Tabela de Alinhamentos

TipoAlinhamento PadrãoTamanho
u8 / i8 / bool11
u16 / i1622
u32 / i32 / f3244
u64 / i64 / f6488
u128 / i1281616
Ponteiro (64-bit)88
@Vector(4, f32)1616

Verificar Alinhamento em Comptime

const std = @import("std");

fn verificarAlinhamento(comptime T: type) void {
    comptime {
        std.debug.assert(@alignOf(T) > 0);
        std.debug.assert(@sizeOf(T) >= @alignOf(T) or @sizeOf(T) == 0);
    }
}

pub fn main() void {
    verificarAlinhamento(u32);
    verificarAlinhamento(u64);
    std.debug.print("u32: align={}, size={}\n", .{ @alignOf(u32), @sizeOf(u32) });
    std.debug.print("u64: align={}, size={}\n", .{ @alignOf(u64), @sizeOf(u64) });
}

Alinhamento Personalizado

// Tipo com alinhamento personalizado
const CacheLine = struct {
    dados: [64]u8 align(64),
};

// Variável com alinhamento personalizado
var buffer: [1024]u8 align(16) = undefined;

// Função que exige alinhamento do argumento
fn processarAlinhado(dados: []align(4) u8) void {
    _ = dados;
}

Erros Relacionados

Continue aprendendo Zig

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