@alignOf em Zig — Referência e Exemplos

@alignOf em Zig

O @alignOf retorna o alinhamento de memória de um tipo em bytes. O alinhamento determina em quais endereços de memória um valor desse tipo pode ser armazenado — o endereço deve ser divisível pelo alinhamento. Esse conceito é fundamental para desempenho e corretude em programação de sistemas.

Sintaxe

@alignOf(comptime T: type) comptime_int

O que faz

O @alignOf consulta o compilador para obter o requisito de alinhamento natural de um tipo em bytes. O alinhamento é sempre uma potência de 2. Tipos mal alinhados podem causar penalidades de desempenho em algumas arquiteturas ou até mesmo falhas de hardware em outras.

Parâmetros

  • T (type, comptime): O tipo cujo alinhamento será consultado. Pode ser qualquer tipo válido em Zig.

Valor de retorno

Retorna um comptime_int representando o alinhamento em bytes. O valor é sempre uma potência de 2.

Exemplos práticos

Exemplo 1: Alinhamento de tipos primitivos

const std = @import("std");

test "alinhamento de tipos primitivos" {
    // Em arquiteturas de 64 bits típicas:
    try std.testing.expect(@alignOf(u8) == 1);
    try std.testing.expect(@alignOf(u16) == 2);
    try std.testing.expect(@alignOf(u32) == 4);
    try std.testing.expect(@alignOf(u64) == 8);
    try std.testing.expect(@alignOf(f32) == 4);
    try std.testing.expect(@alignOf(f64) == 8);
    try std.testing.expect(@alignOf(bool) == 1);
}

Exemplo 2: Alinhamento de structs

const std = @import("std");

const Compacta = struct {
    a: u8,
    b: u8,
};

const ComPadding = struct {
    a: u8,
    b: u64,  // Força alinhamento de 8
    c: u8,
};

test "alinhamento de structs" {
    // Struct alinha pelo campo de maior alinhamento
    try std.testing.expect(@alignOf(Compacta) == 1);
    try std.testing.expect(@alignOf(ComPadding) == 8);

    // Tamanho inclui padding para manter alinhamento
    std.debug.print("Tamanho de Compacta: {}\n", .{@sizeOf(Compacta)});
    std.debug.print("Tamanho de ComPadding: {}\n", .{@sizeOf(ComPadding)});
}

Exemplo 3: Alocação alinhada personalizada

const std = @import("std");

fn alocarAlinhado(
    allocator: std.mem.Allocator,
    comptime T: type,
    n: usize,
) ![]T {
    const alinhamento = @alignOf(T);
    std.debug.print("Alocando {} elementos de {s} com alinhamento {}\n", .{
        n,
        @typeName(T),
        alinhamento,
    });

    return allocator.alloc(T, n);
}

const VetorSimd = @Vector(4, f32);

test "alocação com alinhamento SIMD" {
    const allocator = std.testing.allocator;

    // Vetores SIMD geralmente requerem alinhamento de 16 bytes
    std.debug.print("Alinhamento de @Vector(4, f32): {}\n", .{
        @alignOf(VetorSimd),
    });

    const dados = try alocarAlinhado(allocator, VetorSimd, 100);
    defer allocator.free(dados);

    // Verificar que os dados estão alinhados
    try std.testing.expect(@intFromPtr(dados.ptr) % @alignOf(VetorSimd) == 0);
}

Casos de uso comuns

  1. Alocadores de memória: Garantir que blocos alocados respeitem o alinhamento necessário dos tipos armazenados.
  2. Operações SIMD: Verificar e garantir alinhamento correto para instruções vetoriais que exigem alinhamento de 16 ou 32 bytes.
  3. Interoperabilidade com C: Garantir que structs passadas para código C tenham o alinhamento esperado.
  4. Otimização de layout: Entender o padding inserido pelo compilador para otimizar o layout de structs.
  5. Hardware MMIO: Respeitar requisitos de alinhamento para acesso a registradores de hardware.

@alignOf vs alignof em C

Em C, _Alignof(T) (ou alignof(T) com <stdalign.h>) faz a mesma coisa. O Zig mapeia @alignOf diretamente para o mesmo conceito, mas como builtin comptime — o valor é sempre conhecido em tempo de compilação.

// C
#include <stdalign.h>
size_t al = alignof(uint32_t); // 4

// Zig equivalente
const al = @alignOf(u32); // 4, comptime_int

Como o alinhamento afeta o layout de structs

O compilador insere padding (bytes de preenchimento) entre campos de structs para respeitar os requisitos de alinhamento de cada campo. Entender isso é crucial para otimizar o uso de memória:

const Ineficiente = struct {
    a: u8,   // offset 0, 1 byte
    // 7 bytes de padding aqui
    b: u64,  // offset 8, 8 bytes
    c: u8,   // offset 16, 1 byte
    // 7 bytes de padding no final
};
// @sizeOf(Ineficiente) == 24

const Otimizada = struct {
    b: u64,  // offset 0, 8 bytes
    a: u8,   // offset 8, 1 byte
    c: u8,   // offset 9, 1 byte
    // 6 bytes de padding no final
};
// @sizeOf(Otimizada) == 16

Reordenar campos do maior para o menor alinhamento reduz padding. O @alignOf ajuda a entender e verificar esse layout.

Alinhamento especificado manualmente

O Zig permite especificar alinhamento customizado com a sintaxe align(N):

const Buffer = struct {
    dados: [64]u8 align(64), // alinhado a 64 bytes para cache line
};

var buf: Buffer align(4096) = undefined; // alinhado a página

fn processarSIMD(dados: []align(32) f32) void {
    // garante dados alinhados para AVX2
}

@alignOf é útil para verificar que o alinhamento especificado foi respeitado.

Erros comuns

Assumir alinhamento fixo entre plataformas: @alignOf(usize) é 4 em sistemas 32-bit e 8 em 64-bit. Código que assume um valor fixo pode quebrar em arquiteturas diferentes.

Confundir @sizeOf com @alignOf: @sizeOf(u24) retorna 4 (com padding), mas @alignOf(u24) retorna 1. São valores diferentes com usos diferentes.

Perguntas Frequentes

O alinhamento pode ser zero?

Não. O alinhamento mínimo é sempre 1 (qualquer endereço é válido). Tipos como u8 e bool têm alinhamento 1.

@alignOf funciona com tipos genéricos?

Sim. Como é um builtin comptime, pode ser usado com qualquer tipo, incluindo tipos gerados por funções comptime ou parâmetros genéricos comptime T: type.

Por que alguns tipos têm alinhamento maior que seu tamanho?

Isso não ocorre para tipos padrão no Zig. Mas com align(N) personalizado, o alinhamento pode ser maior que o tamanho. Em C, structs com __attribute__((aligned(N))) têm esse comportamento.

Builtins relacionados

  • @sizeOf — Retorna o tamanho em bytes de um tipo
  • @bitSizeOf — Retorna o tamanho em bits
  • @alignCast — Converte alinhamento de ponteiro
  • @ptrCast — Conversão entre tipos de ponteiro

Tutoriais relacionados

Continue aprendendo Zig

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