stack overflow — Como Resolver em Zig
O Que Este Erro Significa
O erro stack overflow ocorre quando a pilha de execução (stack) do programa excede seu limite de tamanho. Cada chamada de função em Zig aloca espaço na pilha para variáveis locais, parâmetros e endereço de retorno. Quando esse espaço se esgota — geralmente por recursão sem condição de parada ou por alocação de estruturas muito grandes na pilha — o sistema operacional sinaliza um erro de estouro de pilha.
A mensagem pode aparecer como:
thread 1 panic: reached stack guard page
Ou simplesmente um segfault em sistemas que não detectam o estouro graciosamente.
Causas Comuns
1. Recursão Infinita (Sem Caso Base)
fn contar(n: u32) u32 {
return contar(n + 1); // STACK OVERFLOW: nunca para
}
pub fn main() void {
_ = contar(0);
}
2. Caso Base Incorreto na Recursão
fn fatorial(n: u64) u64 {
if (n == 0) return 1;
return n * fatorial(n); // BUG: deveria ser fatorial(n - 1)
// n nunca muda, recursão infinita
}
3. Recursão Mútua Sem Término
fn funcA(n: u32) u32 {
return funcB(n);
}
fn funcB(n: u32) u32 {
return funcA(n); // Loop infinito entre funcA e funcB
}
4. Array Grande Demais na Stack
pub fn main() void {
var buffer: [100_000_000]u8 = undefined; // 100 MB na stack!
// STACK OVERFLOW: a stack padrão é ~8 MB
_ = buffer;
}
5. Struct Grande Passada por Valor
const DadosGrandes = struct {
buffer: [10_000_000]u8, // 10 MB
};
fn processar(dados: DadosGrandes) void { // Copia na stack!
_ = dados;
}
Como Corrigir
Solução 1: Adicionar Caso Base Correto na Recursão
fn fatorial(n: u64) u64 {
if (n <= 1) return 1; // Caso base correto
return n * fatorial(n - 1); // Progride em direção ao caso base
}
Solução 2: Converter Recursão em Iteração
fn fatorial(n: u64) u64 {
var resultado: u64 = 1;
var i: u64 = 2;
while (i <= n) : (i += 1) {
resultado *= i;
}
return resultado;
}
Solução 3: Limitar Profundidade de Recursão
const std = @import("std");
fn buscar(dados: []const u8, profundidade: u32) ?usize {
if (profundidade > 1000) {
std.debug.print("Profundidade máxima alcançada\n", .{});
return null;
}
// ... lógica de busca recursiva ...
return buscar(dados, profundidade + 1);
}
Solução 4: Alocar Dados Grandes no Heap
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Aloca no heap ao invés da stack
const buffer = try allocator.alloc(u8, 100_000_000);
defer allocator.free(buffer);
// Usa buffer normalmente
buffer[0] = 42;
}
Solução 5: Passar Structs Grandes por Ponteiro
const DadosGrandes = struct {
buffer: [10_000_000]u8,
};
fn processar(dados: *const DadosGrandes) void { // Ponteiro: apenas 8 bytes na stack
_ = dados;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const dados = try allocator.create(DadosGrandes);
defer allocator.destroy(dados);
processar(dados);
}
Solução 6: Aumentar o Tamanho da Stack (Thread)
Em casos legítimos de recursão profunda:
const std = @import("std");
pub fn main() void {
const thread = std.Thread.spawn(.{
.stack_size = 64 * 1024 * 1024, // 64 MB de stack
}, funcaoRecursiva, .{}) catch @panic("falha ao criar thread");
thread.join();
}
fn funcaoRecursiva() void {
// Função que precisa de stack grande
}
Tamanho Padrão da Stack
O tamanho padrão da stack varia por plataforma:
| Plataforma | Tamanho Padrão |
|---|---|
| Linux | 8 MB |
| macOS | 8 MB |
| Windows | 1 MB |
Para threads criadas com std.Thread, o padrão pode ser menor (geralmente 2 MB em Linux).
Diagnóstico
Identificar Recursão Infinita
Use @returnAddress ou trace para ver se a mesma função aparece repetidamente na pilha:
const std = @import("std");
fn funcaoSuspeita(n: u32) u32 {
std.debug.print("chamada com n={}\n", .{n});
if (n == 0) return 0;
return funcaoSuspeita(n - 1);
}
Verificar Tamanho de Variáveis Locais
fn exemplo() void {
// Verifique o tamanho antes de alocar na stack
@compileLog(@sizeOf([1_000_000]u8)); // 1 MB — pode ser demais
}
Erros Relacionados
- Segmentation fault — Pode ser consequência de stack overflow
- OutOfMemory — Memória insuficiente no heap
- buffer overrun — Estouro de buffer