@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.

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.