@sizeOf em Zig
O @sizeOf retorna o tamanho de armazenamento de um tipo em bytes, incluindo qualquer padding de alinhamento. É o equivalente do sizeof do C, mas disponível como builtin em tempo de compilação. Fundamental para alocação de memória, serialização e interoperabilidade com código C.
Sintaxe
@sizeOf(comptime T: type) comptime_int
O que faz
O @sizeOf consulta o tamanho que um valor do tipo especificado ocupa na memória, em bytes. O valor retornado inclui qualquer padding inserido pelo compilador para manter o alinhamento correto. Este é o número de bytes que um std.mem.Allocator alocaria para uma instância desse tipo.
Parâmetros
- T (
type, comptime): O tipo cujo tamanho será consultado. Pode ser qualquer tipo válido excetocomptime_int,comptime_floate outros tipos que existem apenas em comptime.
Valor de retorno
Retorna um comptime_int representando o tamanho em bytes do tipo.
Exemplos práticos
Exemplo 1: Tamanho de tipos primitivos
const std = @import("std");
test "tamanho de tipos primitivos" {
try std.testing.expect(@sizeOf(u8) == 1);
try std.testing.expect(@sizeOf(u16) == 2);
try std.testing.expect(@sizeOf(u32) == 4);
try std.testing.expect(@sizeOf(u64) == 8);
try std.testing.expect(@sizeOf(i32) == 4);
try std.testing.expect(@sizeOf(f32) == 4);
try std.testing.expect(@sizeOf(f64) == 8);
try std.testing.expect(@sizeOf(bool) == 1);
// Ponteiros em sistemas de 64 bits
try std.testing.expect(@sizeOf(*u8) == 8);
try std.testing.expect(@sizeOf(usize) == 8);
}
Exemplo 2: Entendendo padding em structs
const std = @import("std");
const SemPadding = struct {
a: u32,
b: u32,
};
const ComPadding = struct {
a: u8, // 1 byte + 7 de padding
b: u64, // 8 bytes
c: u8, // 1 byte + 7 de padding
};
const Otimizada = struct {
b: u64, // 8 bytes
a: u8, // 1 byte
c: u8, // 1 byte + 6 de padding
};
test "padding em structs" {
try std.testing.expect(@sizeOf(SemPadding) == 8); // 4 + 4
std.debug.print("ComPadding: {} bytes\n", .{@sizeOf(ComPadding)});
std.debug.print("Otimizada: {} bytes\n", .{@sizeOf(Otimizada)});
// Reordenar campos pode reduzir o tamanho total
// ComPadding provavelmente é 24 bytes (com padding)
// Otimizada provavelmente é 16 bytes (menos padding)
}
Exemplo 3: Buffer de serialização
const std = @import("std");
const Cabecalho = packed struct {
versao: u8,
tipo: u8,
tamanho: u16,
id: u32,
};
fn serializar(cabecalho: Cabecalho) [@sizeOf(Cabecalho)]u8 {
return @bitCast(cabecalho);
}
fn deserializar(bytes: [@sizeOf(Cabecalho)]u8) Cabecalho {
return @bitCast(bytes);
}
test "serialização com tamanho conhecido" {
// packed struct garante layout previsível
try std.testing.expect(@sizeOf(Cabecalho) == 8);
const cab = Cabecalho{
.versao = 1,
.tipo = 3,
.tamanho = 256,
.id = 42,
};
const bytes = serializar(cab);
const recuperado = deserializar(bytes);
try std.testing.expect(recuperado.versao == 1);
try std.testing.expect(recuperado.id == 42);
}
Casos de uso comuns
- Alocação de memória: Calcular quantos bytes alocar para arrays ou buffers de tipos específicos.
- Serialização binária: Determinar o tamanho de estruturas para leitura/escrita em arquivos ou rede.
- Interoperabilidade com C: Garantir que structs Zig tenham o mesmo tamanho que as equivalentes em C.
- Otimização de structs: Comparar tamanhos com diferentes ordenações de campos para minimizar padding.
- Pools de memória: Dimensionar pools e caches baseados no tamanho dos objetos armazenados.
Diferenças entre @sizeOf e @bitSizeOf
@sizeOfretorna o tamanho em bytes, incluindo padding de alinhamento.@bitSizeOfretorna o tamanho em bits, sem incluir padding extra.- Para um
u24,@sizeOfretorna 4 (arredondado para bytes com alinhamento), enquanto@bitSizeOfretorna 24.
Comportamento com tipos especiais
Alguns tipos merecem atenção especial ao usar @sizeOf:
- Tipos comptime-only (
comptime_int,comptime_float,type):@sizeOfnão é aplicável e causa erro de compilação. void:@sizeOf(void) == 0. Void é um tipo de tamanho zero — útil para containers genéricos onde o tipo pode ser void.noreturn: Erro de compilação, pois não existe instância denoreturn.- Slices (
[]T):@sizeOf([]T) == 2 * @sizeOf(usize)— um slice é fat pointer (ponteiro + comprimento). - Tipos opcionais (
?T): Pode ser maior queTpor adicionar um byte de discriminante, ou igual se T já tiver um valor inválido reservado (null pointer optimization).
const std = @import("std");
test "tamanhos especiais" {
try std.testing.expect(@sizeOf(void) == 0);
try std.testing.expect(@sizeOf([]u8) == 2 * @sizeOf(usize)); // fat pointer
try std.testing.expect(@sizeOf(?*u8) == @sizeOf(*u8)); // null pointer optimization
try std.testing.expect(@sizeOf(?u32) == 8); // u32 + padding + discriminante
}
Otimização de layout de struct
Reordenar campos de uma struct pode reduzir significativamente seu tamanho em memória, eliminando padding desnecessário. A regra geral é ordenar campos do maior para o menor alinhamento:
const std = @import("std");
// Layout ruim: 24 bytes por causa do padding
const Ruim = struct {
flag: bool, // 1 byte + 7 de padding
valor: u64, // 8 bytes
id: u32, // 4 bytes + 4 de padding
};
// Layout otimizado: 16 bytes
const Bom = struct {
valor: u64, // 8 bytes
id: u32, // 4 bytes
flag: bool, // 1 byte + 3 de padding (para alinhar a próxima instância)
};
test "otimização de layout" {
std.debug.print("Ruim: {} bytes\n", .{@sizeOf(Ruim)}); // 24
std.debug.print("Bom: {} bytes\n", .{@sizeOf(Bom)}); // 16
}
Comparação com sizeof do C
O @sizeOf do Zig é semanticamente idêntico ao sizeof do C para tipos compatíveis. Ambos incluem padding de alinhamento. A diferença é que em Zig é um builtin da linguagem, sempre avaliado em comptime, sem necessidade de cabeçalhos:
// C
#include <stdio.h>
printf("%zu\n", sizeof(int)); // 4
printf("%zu\n", sizeof(float)); // 4
// Zig
std.debug.print("{}\n", .{@sizeOf(i32)}); // 4
std.debug.print("{}\n", .{@sizeOf(f32)}); // 4
Para packed struct em Zig, @sizeOf retorna o tamanho exato sem padding extra — equivalente a __attribute__((packed)) no GCC.
Perguntas Frequentes
P: Por que @sizeOf(bool) == 1 se bool só precisa de 1 bit?
R: Em Zig (como em C), o menor endereçável é o byte. Não é possível ter uma variável que ocupa apenas 1 bit de memória endereçável individualmente. Para compactar múltiplos bools, use packed struct ou campos de bits.
P: @sizeOf e @bitSizeOf sempre retornam valores consistentes?
R: Para tipos primitivos, @sizeOf(T) * 8 == @bitSizeOf(T). Para u24, porém, @sizeOf pode retornar 4 (arredondado para alinhamento) enquanto @bitSizeOf retorna 24. Em packed struct, os campos são compactados e @bitSizeOf reflete o tamanho real sem padding.
P: Como usar @sizeOf para calcular o número de elementos que cabem em um buffer?
R: Divida o tamanho do buffer pelo tamanho do elemento:
const BUFFER_SIZE = 4096;
var buffer: [BUFFER_SIZE]u8 = undefined;
const n_elementos = BUFFER_SIZE / @sizeOf(MinhaStruct);
Builtins relacionados
- @bitSizeOf — Retorna o tamanho em bits
- @alignOf — Retorna o alinhamento de um tipo
- @typeInfo — Informações detalhadas sobre o tipo
- @memcpy — Copia bloco de memória