Perguntas de Entrevista sobre Memória em Zig
Gerenciamento de memória é o tema mais frequente e mais importante em entrevistas de programação de sistemas. Zig adota uma abordagem única com seu sistema de allocators explícitos, que é tanto uma das maiores forças da linguagem quanto um dos conceitos mais testados em entrevistas. Domine cada pergunta desta página para se preparar adequadamente.
Conceitos Fundamentais
Explique o padrão de allocator em Zig. Por que Zig não usa malloc/free como C?
Em Zig, funções que precisam alocar memória recebem um Allocator como parâmetro explícito. Isso contrasta com C, onde malloc é uma função global com estado oculto.
fn criarLista(allocator: std.mem.Allocator) !std.ArrayList(u8) {
var list = std.ArrayList(u8).init(allocator);
try list.append(42);
return list;
}
Benefícios do padrão allocator:
- Testabilidade: Testes podem usar allocators que detectam leaks automaticamente.
- Flexibilidade: Diferentes partes do programa podem usar estratégias de alocação diferentes.
- Transparência: É explícito quando e onde alocação acontece — sem surpresas.
- Performance: Permite usar allocators especializados (arena, pool) onde faz sentido.
Qual a diferença entre stack e heap allocation em Zig?
Stack: Memória alocada automaticamente para variáveis locais. Rápida (ajuste de ponteiro), mas limitada em tamanho e escopo. Variáveis morrem ao sair do bloco.
fn exemplo() void {
var buffer: [1024]u8 = undefined; // stack
// buffer é liberado automaticamente ao sair da função
}
Heap: Memória alocada dinamicamente via allocators. Persiste até ser explicitamente liberada. Mais lenta (syscalls), mas flexível em tamanho e lifetime.
fn exemplo(allocator: std.mem.Allocator) !void {
const data = try allocator.alloc(u8, 1024); // heap
defer allocator.free(data);
// ...
}
Quais allocators a biblioteca padrão oferece e quando usar cada um?
page_allocator: Aloca diretamente do OS via mmap/VirtualAlloc. Simples mas lento para alocações pequenas. Use para grandes blocos ou quando não precisa de granularidade fina.GeneralPurposeAllocator: Allocator de propósito geral com detecção de erros (double-free, use-after-free, leaks). Ideal para desenvolvimento e testes.ArenaAllocator: Agrupa alocações e libera tudo de uma vez. Excelente para dados temporários com lifetime uniforme (ex: processar uma requisição HTTP).FixedBufferAllocator: Aloca de um buffer pré-alocado na stack ou heap. Sem syscalls, sem fragmentação. Ideal para embedded e cenários de performance crítica.c_allocator: Wrapper sobre malloc/free de libc. Útil para interop com C.
Perguntas Avançadas
O que é um memory leak e como Zig ajuda a detectá-los?
Memory leak ocorre quando memória alocada nunca é liberada. Em Zig, o GeneralPurposeAllocator em modo debug detecta leaks:
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const leaked = gpa.deinit();
if (leaked == .leak) @panic("Memory leak detectado!");
}
const allocator = gpa.allocator();
Além disso, o padrão defer/errdefer encoraja liberação de memória no ponto de alocação, reduzindo a probabilidade de leaks.
Explique o padrão defer para gerenciamento de recursos.
O padrão idiomático em Zig é parear cada alocação com um defer de liberação imediatamente:
const data = try allocator.alloc(u8, size);
defer allocator.free(data);
// usar data...
Para recursos que devem ser liberados apenas em caso de erro:
const data = try allocator.alloc(u8, size);
errdefer allocator.free(data);
// ... mais código que pode falhar ...
return data; // caller é responsável por liberar
Como implementar um arena allocator e quando usá-lo?
Um arena allocator aloca sequencialmente de um bloco grande e libera tudo de uma vez:
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // libera TUDO
const allocator = arena.allocator();
// Todas as alocações abaixo serão liberadas juntas
const a = try allocator.alloc(u8, 100);
const b = try allocator.alloc(u8, 200);
// Não precisa de free individual
Casos de uso: Processamento de requisições (cada requisição tem uma arena), compiladores (cada fase tem uma arena), game loops (dados temporários por frame).
O que é use-after-free e como Zig o previne?
Use-after-free ocorre quando código acessa memória que já foi liberada. Em Zig, o GeneralPurposeAllocator em modo debug preenche memória liberada com valores sentinela, causando crash previsível em vez de comportamento indefinido.
A linguagem também previne muitos cenários via análise em tempo de compilação — por exemplo, retornar ponteiro para variável local é erro de compilação.
Explique a diferença entre []T, [*]T, *T e *[N]T.
[]T(slice): Ponteiro + comprimento. A forma mais segura e idiomática de referenciar dados contíguos.[*]T(many-item pointer): Ponteiro para zero ou mais itens. Sem informação de comprimento — usado em interop com C.*T(single-item pointer): Ponteiro para exatamente um item.*[N]T(pointer to array): Ponteiro para um array de tamanho N conhecido em compilação.
Cenários Práticos
Como evitar fragmentação de memória em software de longo prazo?
Estratégias incluem:
- Usar arena allocators para alocações de vida curta
- Pool allocators para objetos de tamanho fixo
- Pré-alocar buffers de tamanho conhecido
- Evitar alocações no hot path
- Monitorar com ferramentas de profiling
Como gerenciar memória em sistemas embarcados com Zig?
Em embedded com RAM limitada:
- Use
FixedBufferAllocatorcom buffer estático - Prefira alocação em stack quando possível
- Evite alocação dinâmica no runtime para sistemas safety-critical
- Use comptime para calcular tamanhos necessários em tempo de compilação
Preparação Complementar
Estude também:
- Error handling — intimamente ligado a memória (errdefer)
- Performance — alocação afeta diretamente performance
- Allocator libs do ecossistema
- Desafios de código — pratique gerenciamento de memória
- Tutoriais e receitas para exemplos práticos