std.heap em Zig — Referência e Exemplos

std.heap — Alocadores de Memória

O módulo std.heap fornece implementações concretas da interface std.mem.Allocator. Cada alocador tem características diferentes, adequadas para cenários específicos de uso. A escolha correta do alocador impacta desempenho, uso de memória e facilidade de depuração.

Visão Geral

const std = @import("std");
const heap = std.heap;

O Zig oferece vários alocadores na biblioteca padrão, cada um com trade-offs diferentes:

AlocadorUso PrincipalOverheadDetecção de Bugs
GeneralPurposeAllocatorDesenvolvimento e uso geralMédioSim
ArenaAllocatorAlocações temporárias em grupoBaixoNão
page_allocatorAlocações grandes ou base para outrosMínimoNão
FixedBufferAllocatorSem syscalls, buffer pré-alocadoZeroNão
c_allocatorInterop com CMínimoNão

Alocadores Disponíveis

heap.GeneralPurposeAllocator

Alocador de uso geral com detecção de vazamentos de memória, double-free e uso após free. Ideal para desenvolvimento e depuração.

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

heap.ArenaAllocator

Alocador que permite liberar toda a memória de uma vez. Alocações individuais não são liberadas — apenas deinit() libera tudo.

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();

heap.page_allocator

Alocador que usa diretamente páginas de memória do sistema operacional via mmap/VirtualAlloc. Não tem estado — é uma constante global.

const allocator = std.heap.page_allocator;

heap.FixedBufferAllocator

Alocador sobre um buffer de tamanho fixo. Não faz nenhuma syscall.

var buf: [4096]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
const allocator = fba.allocator();

heap.c_allocator

Wrapper sobre malloc/free do C. Útil para interoperabilidade com bibliotecas C.

const allocator = std.heap.c_allocator;

Exemplo 1: Comparando Alocadores

const std = @import("std");

fn usarAlocador(allocator: std.mem.Allocator) !void {
    var lista = std.ArrayList(u32).init(allocator);
    defer lista.deinit();

    for (0..100) |i| {
        try lista.append(@intCast(i * 2));
    }

    std.debug.print("Lista com {d} itens, último: {d}\n", .{
        lista.items.len,
        lista.items[lista.items.len - 1],
    });
}

pub fn main() !void {
    // Com GeneralPurposeAllocator
    {
        var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        defer _ = gpa.deinit();
        std.debug.print("GPA: ", .{});
        try usarAlocador(gpa.allocator());
    }

    // Com ArenaAllocator
    {
        var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
        defer arena.deinit();
        std.debug.print("Arena: ", .{});
        try usarAlocador(arena.allocator());
    }

    // Com FixedBufferAllocator
    {
        var buf: [8192]u8 = undefined;
        var fba = std.heap.FixedBufferAllocator.init(&buf);
        std.debug.print("FBA: ", .{});
        try usarAlocador(fba.allocator());
    }

    // Com page_allocator
    {
        std.debug.print("Page: ", .{});
        try usarAlocador(std.heap.page_allocator);
    }
}

Exemplo 2: Escolha de Alocador por Contexto

const std = @import("std");

fn processarRequisicao(allocator: std.mem.Allocator) ![]u8 {
    // Arena para dados temporários da requisição
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();
    const temp = arena.allocator();

    // Alocações temporárias — serão todas liberadas no defer
    const header = try temp.alloc(u8, 256);
    const body = try temp.alloc(u8, 1024);
    _ = header;
    _ = body;

    // O resultado final usa o alocador do chamador
    const resultado = try allocator.dupe(u8, "Resposta processada");
    return resultado;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const resp = try processarRequisicao(allocator);
    defer allocator.free(resp);

    std.debug.print("{s}\n", .{resp});
}

Exemplo 3: Detecção de Vazamentos com GPA

const std = @import("std");

pub fn main() void {
    var gpa = std.heap.GeneralPurposeAllocator(.{
        .verbose_log = true, // Log detalhado (opcional)
    }){};
    defer {
        const status = gpa.deinit();
        switch (status) {
            .ok => std.debug.print("Sem vazamentos!\n", .{}),
            .leak => std.debug.print("VAZAMENTO detectado!\n", .{}),
        }
    }
    const allocator = gpa.allocator();

    // Alocação que será liberada corretamente
    const dados1 = allocator.alloc(u8, 100) catch return;
    allocator.free(dados1);

    // Alocação que NÃO será liberada (vazamento intencional para demonstração)
    _ = allocator.alloc(u8, 50) catch return;

    // O defer do gpa.deinit() reportará o vazamento
}

Padrões Comuns

Hierarquia de Alocadores

// Base: page_allocator (syscalls)
// -> GPA (detecção de bugs em desenvolvimento)
//   -> Arena (alocações temporárias por escopo)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var arena = std.heap.ArenaAllocator.init(gpa.allocator());

Alocador para Testes

test "minha função" {
    // testing.allocator detecta vazamentos automaticamente
    const resultado = try minhaFuncao(std.testing.allocator);
    defer std.testing.allocator.free(resultado);
    try std.testing.expect(resultado.len > 0);
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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