Packed Struct Layout — Como Resolver em Zig

Packed Struct Layout — Como Resolver em Zig

O Que Este Erro Significa

Problemas com packed structs em Zig ocorrem quando o programador não respeita as restrições especiais desse tipo de struct. Uma packed struct em Zig armazena seus campos em um layout compacto, sem padding entre campos e com bit-packing. Isso é útil para protocolos de rede, hardware e formatos binários, mas vem com limitações.

Mensagens de erro comuns:

error: packed structs cannot contain fields of type '[]const u8'
error: pointer to packed struct field has non-standard alignment
error: cannot take address of packed struct field

Causas Comuns

1. Campos com Tipos Proibidos em Packed Struct

const Pacote = packed struct {
    tipo: u8,
    dados: []const u8, // ERRO: slices não são permitidos em packed struct
};

2. Tentar Obter Ponteiro para Campo de Packed Struct

const Registrador = packed struct {
    bit_a: u1,
    bit_b: u1,
    valor: u6,
};

pub fn main() void {
    var reg = Registrador{ .bit_a = 1, .bit_b = 0, .valor = 30 };
    const ptr = &reg.valor; // ERRO: não pode obter ponteiro para campo sub-byte
    _ = ptr;
}

3. Campos de Struct Regular dentro de Packed

const Inner = struct { // struct regular com padding
    x: u32,
    y: u8,
};

const Outer = packed struct {
    cabecalho: u8,
    dados: Inner, // ERRO: struct regular dentro de packed
};

4. Campos com Alinhamento Especial

const Pacote = packed struct {
    versao: u4,
    tipo: u4,
    dados: [100]u8, // ERRO: arrays grandes em packed struct
};

5. Usar Optional ou Error Union em Packed

const Estado = packed struct {
    ativo: bool,
    valor: ?u8, // ERRO: optional não é permitido em packed struct
};

Como Corrigir

Solucao 1: Usar Apenas Tipos de Tamanho Fixo

const Pacote = packed struct {
    versao: u4,    // 4 bits
    tipo: u4,      // 4 bits
    flags: u8,     // 8 bits
    tamanho: u16,  // 16 bits
    // Total: 32 bits = 4 bytes
};

pub fn main() void {
    const p = Pacote{
        .versao = 1,
        .tipo = 3,
        .flags = 0xFF,
        .tamanho = 1024,
    };
    _ = p;
}

Solucao 2: Packed Struct dentro de Packed

const Flags = packed struct {
    leitura: bool,    // 1 bit
    escrita: bool,    // 1 bit
    execucao: bool,   // 1 bit
    _reservado: u5,   // 5 bits para completar 1 byte
};

const Cabecalho = packed struct {
    tipo: u8,
    flags: Flags,   // OK: packed struct dentro de packed
    tamanho: u16,
};

pub fn main() void {
    const cab = Cabecalho{
        .tipo = 1,
        .flags = .{ .leitura = true, .escrita = false, .execucao = true, ._reservado = 0 },
        .tamanho = 256,
    };
    std.debug.print("Tamanho da struct: {} bytes\n", .{@sizeOf(Cabecalho)});
    _ = cab;
}

const std = @import("std");

Solucao 3: Acessar Campos sem Ponteiro

const Registrador = packed struct {
    bit_a: u1,
    bit_b: u1,
    valor: u6,
};

pub fn main() void {
    var reg = Registrador{ .bit_a = 1, .bit_b = 0, .valor = 30 };

    // ERRADO: &reg.valor — ponteiro para campo sub-byte
    // CORRETO: ler e escrever diretamente
    const v = reg.valor;  // Lê o campo
    reg.valor = v + 1;    // Escreve no campo
    _ = reg;
}

Solucao 4: Converter entre Packed Struct e Inteiro

const Flags = packed struct {
    bit0: u1,
    bit1: u1,
    bit2: u1,
    bit3: u1,
    upper: u4,
};

pub fn main() void {
    // De inteiro para packed struct
    const raw: u8 = 0b1010_0101;
    const flags: Flags = @bitCast(raw);
    _ = flags;

    // De packed struct para inteiro
    const f = Flags{ .bit0 = 1, .bit1 = 0, .bit2 = 1, .bit3 = 0, .upper = 0xA };
    const byte: u8 = @bitCast(f);
    _ = byte;
}

Solucao 5: Usar extern struct Quando Precisa de Ponteiros

// Quando precisa de ponteiros para campos, use extern struct
const PacoteExtern = extern struct {
    tipo: u32,
    tamanho: u32,
    flags: u32,
};

pub fn main() void {
    var pac = PacoteExtern{ .tipo = 1, .tamanho = 100, .flags = 0 };
    const ptr_tipo = &pac.tipo; // OK: campos de extern struct são endereçáveis
    ptr_tipo.* = 2;
    _ = pac;
}

Solucao 6: Separar Dados Packed e Não-Packed

const std = @import("std");

// Packed para o cabeçalho binário
const CabecalhoBin = packed struct {
    versao: u4,
    tipo: u4,
    flags: u8,
    tamanho: u16,
};

// Regular para uso interno
const Pacote = struct {
    cabecalho: CabecalhoBin,
    dados: []const u8, // Slice é OK em struct regular
    allocator: std.mem.Allocator,

    fn fromBytes(bytes: []const u8, allocator: std.mem.Allocator) !Pacote {
        if (bytes.len < @sizeOf(CabecalhoBin)) return error.DadosInsuficientes;
        const cab: CabecalhoBin = @bitCast(bytes[0..@sizeOf(CabecalhoBin)].*);
        return .{
            .cabecalho = cab,
            .dados = bytes[@sizeOf(CabecalhoBin)..],
            .allocator = allocator,
        };
    }
};

Packed vs Extern vs Regular

Propriedadestructextern structpacked struct
PaddingSim (Zig decide)Segue ABI CNenhum
Ponteiro para campoSimSimLimitado
Bit fieldsNãoNãoSim
Slices/OptionalsSimNãoNão
Uso típicoCódigo ZigFFI com CHardware/protocolos

Tamanho de Packed Struct

const std = @import("std");

const Exemplo = packed struct {
    a: u3,   // 3 bits
    b: u5,   // 5 bits
    c: u8,   // 8 bits
    d: u16,  // 16 bits
    // Total: 32 bits = 4 bytes
};

pub fn main() void {
    std.debug.print("Tamanho: {} bytes\n", .{@sizeOf(Exemplo)}); // 4
    std.debug.print("Bits: {}\n", .{@bitSizeOf(Exemplo)});       // 32
}

Erros Relacionados

Continue aprendendo Zig

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