---
title: "Substituir malloc/free por Allocators Zig"
url: "https://ziglang.com.br/tutoriais/substituir-malloc/free-por-allocators-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/substituir-malloc/free-por-allocators-zig.MD"
description: "Guia prático para substituir malloc/free de C por allocators de Zig. Entenda GeneralPurposeAllocator, ArenaAllocator, FixedBufferAllocator e padrões de gerenciamento de memória."
date: "2026-02-21"
author: "Zig Brasil"
---

# Substituir malloc/free por Allocators Zig

Guia prático para substituir malloc/free de C por allocators de Zig. Entenda GeneralPurposeAllocator, ArenaAllocator, FixedBufferAllocator e padrões de gerenciamento de memória.


## Introdução

Uma das maiores diferenças entre C e Zig é o gerenciamento de memória. Em C, `malloc` e `free` são funções globais — qualquer código pode alocar memória de qualquer lugar. Em Zig, toda alocação é feita através de um **allocator** passado explicitamente como parâmetro.

Este guia mostra como substituir os padrões de malloc/free por allocators Zig. Para detalhes sobre cada allocator, consulte [ArenaAllocator](/receitas/zig-arena-allocator/), [GeneralPurposeAllocator](/receitas/zig-general-purpose-allocator/) e [FixedBufferAllocator](/receitas/zig-fixed-buffer-allocator/).

## Pré-requisitos

- Zig instalado (versão 0.13+). Veja [Como Instalar Zig](/tutoriais/como-instalar-zig/)
- Conhecimento básico de C e alocação de memória
- Familiaridade com Zig. Consulte [Introdução ao Zig](/tutoriais/introducao-ao-zig/)

## O Problema com malloc/free

Em C, alocação de memória é invisível e global:

```c
// Quem libera? Quando? Nenhuma informação no tipo.
char* ler_arquivo(const char* caminho) {
    FILE* f = fopen(caminho, "r");
    // ...
    char* conteudo = malloc(tamanho);
    fread(conteudo, 1, tamanho, f);
    fclose(f);
    return conteudo; // O chamador precisa saber que deve chamar free()
}
```

Problemas comuns:
- **Double free**: Liberar a mesma memória duas vezes
- **Use-after-free**: Usar memória já liberada
- **Memory leak**: Esquecer de liberar
- **Invisibilidade**: Não é claro quais funções alocam
- **Não testável**: Não dá para substituir o alocador em testes

## A Solução: Allocators Explícitos

Em Zig, o allocator é um parâmetro explícito:

```zig
fn lerArquivo(allocator: std.mem.Allocator, caminho: []const u8) ![]u8 {
    const arquivo = try std.fs.cwd().openFile(caminho, .{});
    defer arquivo.close();
    return arquivo.readToEndAlloc(allocator, 1024 * 1024);
}

// O chamador SABE que memória foi alocada e é responsável por liberar:
const conteudo = try lerArquivo(allocator, "dados.txt");
defer allocator.free(conteudo);
```

## Mapeamento malloc/free para Zig

### Alocação Simples

```c
// C
int* ptr = (int*)malloc(sizeof(int));
*ptr = 42;
free(ptr);
```

```zig
// Zig
const ptr = try allocator.create(i32);
defer allocator.destroy(ptr);
ptr.* = 42;
```

### Alocação de Array

```c
// C
int* arr = (int*)malloc(100 * sizeof(int));
memset(arr, 0, 100 * sizeof(int));
free(arr);
```

```zig
// Zig
const arr = try allocator.alloc(i32, 100);
defer allocator.free(arr);
@memset(arr, 0);
```

### Realloc

```c
// C
int* arr = malloc(10 * sizeof(int));
arr = realloc(arr, 20 * sizeof(int));
free(arr);
```

```zig
// Zig
var arr = try allocator.alloc(i32, 10);
arr = try allocator.realloc(arr, 20);
defer allocator.free(arr);
```

### Strings Dinâmicas

```c
// C
char* msg = malloc(256);
snprintf(msg, 256, "Olá, %s!", nome);
free(msg);
```

```zig
// Zig
const msg = try std.fmt.allocPrint(allocator, "Olá, {s}!", .{nome});
defer allocator.free(msg);
```

## Escolhendo o Allocator Certo

### GeneralPurposeAllocator (GPA)

Equivalente mais próximo ao malloc, com detecção de bugs em debug:

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
    const status = gpa.deinit();
    if (status == .leak) {
        std.debug.print("Vazamento de memória detectado!\n", .{});
    }
}
const allocator = gpa.allocator();
```

**Use para**: aplicações de uso geral, desenvolvimento, detecção de bugs.

### ArenaAllocator

Aloca tudo e libera de uma vez. Ideal para processamento em fases:

```zig
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Libera TUDO de uma vez

const allocator = arena.allocator();
// Alocar livremente sem se preocupar com free individual
const a = try allocator.alloc(u8, 100);
const b = try allocator.alloc(u8, 200);
const c = try allocator.alloc(u8, 300);
// Tudo liberado no arena.deinit()
```

**Use para**: parsing, processamento de requisições, operações com lifecycle definido. Veja [ArenaAllocator](/receitas/zig-arena-allocator/).

### FixedBufferAllocator

Aloca de um buffer fixo na stack, sem heap:

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

const dados = try allocator.alloc(u8, 100);
// Sem heap allocation — tudo na stack
```

**Use para**: sistemas embarcados, hot paths, quando heap não é desejável. Veja [FixedBufferAllocator](/receitas/zig-fixed-buffer-allocator/).

### page_allocator

Aloca diretamente do sistema operacional em páginas:

```zig
const allocator = std.heap.page_allocator;
const dados = try allocator.alloc(u8, 4096);
defer allocator.free(dados);
```

**Use para**: alocações grandes, backing allocator para arenas.

## Padrão de Migração

### Passo 1: Identificar Funções que Alocam

Em C, busque por `malloc`, `calloc`, `realloc`, `strdup`, e qualquer função que retorne ponteiros heap-alocados.

### Passo 2: Adicionar Allocator como Parâmetro

```zig
// Antes (tradução direta de C)
fn processar() ![]u8 {
    return std.heap.c_allocator.alloc(u8, 100); // RUIM: alocador hardcoded
}

// Depois (idiomático Zig)
fn processar(allocator: std.mem.Allocator) ![]u8 {
    return allocator.alloc(u8, 100); // BOM: alocador injetado
}
```

### Passo 3: Usar defer/errdefer

```zig
fn criarRecurso(allocator: std.mem.Allocator) !*Recurso {
    const r = try allocator.create(Recurso);
    errdefer allocator.destroy(r); // Libera se o resto falhar

    r.* = .{
        .dados = try allocator.alloc(u8, 1024),
        .estado = .inicializando,
    };
    errdefer allocator.free(r.dados);

    try r.inicializar();
    return r;
}
```

Veja [Padrões Errdefer](/receitas/zig-errdefer-pattern/).

### Passo 4: Testar com Allocators de Teste

```zig
test "processar não vaza memória" {
    // testing.allocator detecta leaks automaticamente
    const resultado = try processar(std.testing.allocator);
    defer std.testing.allocator.free(resultado);

    try std.testing.expect(resultado.len == 100);
}
```

Veja [Testes com Allocator](/receitas/zig-teste-com-allocator/) e [Detectar Vazamentos](/receitas/zig-detectar-memory-leak/).

## Padrões Avançados

### Arena por Requisição

```zig
fn processarRequisicao(req: Requisicao) !Resposta {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const alloc = arena.allocator();

    // Todas as alocações desta requisição usam a arena
    const parsed = try parsearBody(alloc, req.body);
    const resultado = try executarLogica(alloc, parsed);
    return criarResposta(resultado);
}
```

### Struct com Allocator

```zig
const MinhaStruct = struct {
    allocator: std.mem.Allocator,
    dados: []u8,

    pub fn init(allocator: std.mem.Allocator) !MinhaStruct {
        return .{
            .allocator = allocator,
            .dados = try allocator.alloc(u8, 256),
        };
    }

    pub fn deinit(self: *MinhaStruct) void {
        self.allocator.free(self.dados);
    }
};
```

## Conclusão

Substituir malloc/free por allocators Zig melhora significativamente a segurança e testabilidade do código. O allocator como parâmetro explícito torna cada alocação visível e permite trocar estratégias de memória sem mudar a lógica do programa.

Para mais sobre gerenciamento de memória, veja [Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/). Para migração de projeto, consulte [Guia de Migração: C para Zig](/tutoriais/migrar-de-c-para-zig/).
