Packed Struct em Zig — O que é e Como Usar
Definição
Uma packed struct em Zig é uma struct declarada com a palavra-chave packed que possui um layout de memória exato e previsível, sem padding (bytes de preenchimento) entre os campos. Os campos são empacotados bit a bit, permitindo definir campos com tamanhos arbitrários em bits — como campos de 3 bits, 1 bit (booleano) ou 5 bits.
Packed structs são fundamentais para trabalhar com protocolos de rede, formatos de arquivo binário, registradores de hardware e qualquer situação onde o layout exato dos bits importa.
Por que Packed Structs Importam
- Layout garantido: Você sabe exatamente onde cada bit está na memória.
- Bitfields: Campos podem ter tamanhos menores que 1 byte (ex:
u3,u1). - Interoperabilidade: Mapeiam diretamente para estruturas de protocolos binários.
- Conversão segura: Podem ser convertidas de/para inteiros com
@bitCast. - Hardware: Permitem mapear registradores de dispositivos com precisão bit a bit.
Exemplo Prático
Bitfields para Protocolo
const std = @import("std");
const CabecalhoTCP = packed struct {
porta_origem: u16,
porta_destino: u16,
numero_sequencia: u32,
numero_ack: u32,
offset: u4,
reservado: u3,
flags: packed struct {
ns: u1,
cwr: u1,
ece: u1,
urg: u1,
ack: u1,
psh: u1,
rst: u1,
syn: u1,
fin: u1,
},
janela: u16,
checksum: u16,
ponteiro_urgente: u16,
};
pub fn main() void {
std.debug.print("Tamanho do cabeçalho: {} bytes\n", .{@sizeOf(CabecalhoTCP)});
}
Conversão para Inteiro
const Flags = packed struct {
leitura: bool, // 1 bit
escrita: bool, // 1 bit
execucao: bool, // 1 bit
_padding: u5 = 0, // 5 bits para completar 1 byte
};
pub fn main() void {
const flags = Flags{
.leitura = true,
.escrita = true,
.execucao = false,
};
// Converter para inteiro
const valor: u8 = @bitCast(flags);
std.debug.print("Flags como byte: 0b{b:0>8}\n", .{valor});
// Saída: 0b00000011
}
Lendo Dados Binários
fn lerCabecalho(dados: []const u8) *const CabecalhoTCP {
return @ptrCast(@alignCast(dados.ptr));
}
Packed vs Extern vs Normal
// Normal: compilador pode reordenar e adicionar padding
const Normal = struct {
a: u8,
b: u32, // padding de 3 bytes antes de b
c: u8,
};
// Packed: sem padding, campos empacotados bit a bit
const Packed = packed struct {
a: u8,
b: u32,
c: u8,
};
// Extern: layout compatível com C ABI
const Extern = extern struct {
a: u8,
b: u32,
c: u8,
};
// @sizeOf(Normal) >= 12 (com padding)
// @sizeOf(Packed) == 5 (sem padding)
// @sizeOf(Extern) == 12 (padding C)
Armadilhas Comuns
- Performance: Acessar campos não alinhados em packed structs pode ser mais lento que structs normais. O processador pode precisar de instruções extras.
- Ponteiros para campos: Não é possível obter ponteiros para campos individuais de uma packed struct que não estejam alinhados a bytes.
- Endianness: Packed structs usam a endianness nativa. Para protocolos de rede (big-endian), use
std.mem.bigToNativepara converter. - Confundir com
extern struct: Para interop com C, useextern struct. Packed structs são para controle bit-a-bit. - Bool em packed struct:
boolocupa 1 bit, não 1 byte como em structs normais.
Termos Relacionados
- Struct — Structs regulares em Zig
- Alignment — Alinhamento de memória
- Union — Tipos union em Zig
- Slice — Referências a sequências de memória