---
title: "Dangling Pointer em Zig — O que é e Como Evitar"
url: "https://ziglang.com.br/glossario/dangling-pointer-em-zig-o-que-%C3%A9-e-como-evitar/"
markdown_url: "https://ziglang.com.br/glossario/dangling-pointer-em-zig-o-que-%C3%A9-e-como-evitar.MD"
description: "Entenda dangling pointers em Zig: ponteiros que referenciam memória já liberada ou inválida. Causas, detecção e prevenção. Guia em pt-BR."
date: "2026-02-21"
author: "Zig Brasil"
---

# Dangling Pointer em Zig — O que é e Como Evitar

Entenda dangling pointers em Zig: ponteiros que referenciam memória já liberada ou inválida. Causas, detecção e prevenção. Guia em pt-BR.


# Dangling Pointer em Zig — O que é e Como Evitar

## Definição

Um **dangling pointer** (ponteiro pendente) é um ponteiro que referencia uma posição de memória que **já foi liberada ou não é mais válida**. Desreferenciar um dangling pointer é comportamento indefinido: o programa pode crashar, retornar lixo ou, pior, parecer funcionar corretamente enquanto corrompe dados silenciosamente.

Em Zig, dangling pointers podem surgir de três formas principais: use-after-free (usar após liberar), retorno de ponteiro para variável local e invalidação de ponteiro por realocação.

## Por que Dangling Pointers Importam

1. **Bugs catastróficos**: Podem causar crashes aleatórios, corrupção de dados e vulnerabilidades de segurança.
2. **Difícil reprodução**: O comportamento depende do estado da memória — pode funcionar em dev e falhar em produção.
3. **Segurança**: Dangling pointers são a base de muitos exploits de segurança (use-after-free).
4. **Sem garbage collector**: Em Zig, o programador é responsável por evitá-los.

## Exemplo Prático

### Use-After-Free

```zig
const std = @import("std");

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

    const dados = try allocator.alloc(u8, 100);
    allocator.free(dados);

    // PERIGO: dados agora é um dangling pointer!
    // dados[0] = 'X'; // Comportamento indefinido!
    // Em Debug, GPA detecta e causa panic
}
```

### Ponteiro para Variável Local

```zig
fn perigoso() *u32 {
    var local: u32 = 42;
    return &local; // ERRO: local é destruída ao sair da função!
}

// Correto: retornar o valor, não o ponteiro
fn seguro() u32 {
    var local: u32 = 42;
    return local; // Cópia do valor
}

// Ou: alocar no heap
fn seguro_heap(allocator: std.mem.Allocator) !*u32 {
    const ptr = try allocator.create(u32);
    ptr.* = 42;
    return ptr; // Dados vivem no heap
}
```

### Invalidação por Realocação

```zig
const std = @import("std");

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

    var lista = std.ArrayList(u32).init(gpa.allocator());
    defer lista.deinit();

    try lista.append(10);
    try lista.append(20);

    // Pegamos um ponteiro para o buffer interno
    const ptr = &lista.items[0];

    // Append pode realocar o buffer interno!
    try lista.ensureTotalCapacity(1000);

    // PERIGO: ptr pode ser dangling se houve realocação!
    // _ = ptr.*; // Potencialmente inválido
    _ = ptr;
}
```

### Slice de Array Local

```zig
fn perigoso2() []const u8 {
    var buffer: [100]u8 = undefined;
    @memcpy(buffer[0..5], "Hello");
    return buffer[0..5]; // PERIGO: buffer é destruído!
}

fn seguro2(allocator: std.mem.Allocator) ![]u8 {
    const buffer = try allocator.alloc(u8, 5);
    @memcpy(buffer, "Hello");
    return buffer; // OK: memória vive no heap
}
```

## Proteções do Zig

| Proteção | Modo | Efeito |
|----------|------|--------|
| GPA use-after-free | Debug | Panic ao acessar memória liberada |
| GPA double-free | Debug | Panic ao liberar duas vezes |
| Compilador | Todos | Erro ao retornar ponteiro para local |
| Bounds checking | Debug/ReleaseSafe | Panic em acesso fora dos limites |

## Como Prevenir

1. **Use `defer`**: Libere recursos no final do escopo, não antes.
2. **Não retenha ponteiros para internos de coleções**: Após `append`, `resize` ou `ensureCapacity`, ponteiros antigos podem ser inválidos.
3. **Prefira valores a ponteiros**: Retorne cópias quando os dados são pequenos.
4. **Use arena allocator**: Quando todos os dados têm o mesmo tempo de vida.
5. **Teste com GPA**: O `GeneralPurposeAllocator` detecta muitos casos de dangling pointer.

## Como o GPA Detecta Dangling Pointers

O `GeneralPurposeAllocator` em modo Debug preenche a memória liberada com um padrão de bytes específico (`0xaa` por padrão). Se código posterior lê essa memória e encontra esse padrão, é um sinal claro de use-after-free. Além disso, o GPA mantém metadados sobre cada alocação ativa, detectando double-free imediatamente:

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{
        .safety = true, // Ativado por padrão em Debug
    }){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();
    const dados = try allocator.alloc(u8, 10);

    allocator.free(dados);

    // Em modo Debug, a próxima linha causaria panic imediato:
    // dados[0] = 'X'; // "use after free"
}
```

## Boas Práticas

- **Invalide ponteiros após liberar**: Após `allocator.free(ptr)`, atribua `null` se estiver usando `?*T`. Isso transforma um potencial bug silencioso em um erro de compilação ou panic explícito.
- **Nunca guarde ponteiros para internos de ArrayList**: Após qualquer operação que possa realocar (`append`, `ensureTotalCapacity`, `resize`), ponteiros e slices para `lista.items` ficam inválidos. Releia-os.
- **Prefira índices a ponteiros em coleções mutáveis**: Em vez de guardar `&lista.items[i]`, guarde `i`. O índice continua válido mesmo após realloc.
- **Use o AddressSanitizer (ASan)**: Com `zig build -Doptimize=Debug`, combine com ASan para detectar dangling pointers mesmo em código C interoperável via `zig build -fsanitize-c`.

## Armadilhas Comuns

- **Funciona em Debug, falha em Release**: O GPA preenche memória liberada com padrão detectável. Em Release, a memória pode ainda conter os dados antigos, mascarando o bug.
- **Slices são ponteiros**: Um `[]u8` é um ponteiro + length. Se a memória apontada for liberada, o slice é dangling.
- **Closures com ponteiros locais**: Capturar `&variavel_local` em um contexto que sobrevive ao escopo cria dangling pointer.
- **Confundir com null pointer**: Dangling pointers têm endereço "válido" (não-zero), mas apontam para memória inválida. `?*T` (optional pointer) resolve o problema de null, não de dangling.

## Termos Relacionados

- [Memory Leak](/glossario/memory-leak/) — Memória alocada nunca liberada
- [Allocator](/glossario/allocator/) — Interface de gerenciamento de memória
- [Defer](/glossario/defer/) — Limpeza garantida ao sair do escopo
- [Slice](/glossario/slice/) — Referência a sequência de memória
- [Stack vs Heap](/glossario/stack-vs-heap/) — Tempo de vida da memória

## Tutoriais Relacionados

- [Zig Segurança de Memória](/tutoriais/zig-seguranca-memoria/)
- [Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)
- [Zig Debugging](/tutoriais/zig-debugging/)
