O erroFalha de segmentação (núcleo despejado) em C ou C++ significa que seu programa tentou acessar a memória que não era permitido. É um dos erros C/C++ mais comuns e frustrantes. Veja como diagnosticar e corrigir todas as causas comuns.
📋 Table of Contents
- O que realmente é um Segfault
- Causa 1: Desreferenciando um ponteiro NULL
- Causa 2: Buffer Overflow (acesso fora dos limites)
- Causa 3: Use-After-Free (ponteiro pendente)
- Causa 4: estouro de pilha (recursão infinita)
- Depurando com gdb
- Encontrando Bugs de Memória com Valgrind
- Usando AddressSanitizer (rápido, integrado)
- Perguntas Frequentes
- Conclusão
O que realmente é um Segfault
Uma falha de segmentação ocorre quando seu programa acessa a memória fora do que o sistema operacional concedeu – desreferenciando um ponteiro inválido, escrevendo além dos limites de um array ou usando memória liberada. O sistema operacional mata o programa para proteger o sistema e despeja seu estado (o “core dump”).
Causa 1: Desreferenciando um ponteiro NULL
// 🐛 Accessing a NULL or uninitialized pointer
int *ptr = NULL;
*ptr = 42; // ❌ segfault — writing to address 0
int *uninitialized; // points to garbage
*uninitialized = 5; // ❌ segfault (undefined behavior)
// ✅ Always initialize and check pointers
int value = 42;
int *ptr = &value
if (ptr != NULL) {
*ptr = 10; // safe
}
Causa 2: Buffer Overflow (acesso fora dos limites)
// 🐛 Writing past the end of an array
int arr[5];
for (int i = 0; i <= 5; i++) { // ❌ i=5 is out of bounds
arr[i] = i; // arr[5] doesn't exist
}
// ✅ Correct bounds
for (int i = 0; i < 5; i++) { // i < 5, not <=
arr[i] = i;
}
// 🐛 strcpy overflow
char dest[5];
strcpy(dest, "This is too long"); // ❌ overflows dest
// ✅ Use bounded copy
char dest[20];
strncpy(dest, "safe copy", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
Causa 3: Use-After-Free (ponteiro pendente)
// 🐛 Using memory after freeing it
int *ptr = malloc(sizeof(int));
*ptr = 42;
free(ptr);
*ptr = 10; // ❌ segfault — ptr is dangling
// ✅ Set to NULL after freeing
free(ptr);
ptr = NULL; // prevents accidental reuse
// if (ptr) *ptr = 10; // now safely skipped
Causa 4: estouro de pilha (recursão infinita)
// 🐛 Recursion with no base case exhausts the stack
int factorial(int n) {
return n * factorial(n - 1); // ❌ never stops → stack overflow
}
// ✅ Add a base case
int factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1);
}
// 🐛 Huge local array can also overflow the stack
void func() {
int huge[10000000]; // ❌ too big for the stack
}
// ✅ Allocate large data on the heap
void func() {
int *huge = malloc(10000000 * sizeof(int));
// ... use it ...
free(huge);
}
Depurando com gdb
# Compile with debug symbols
gcc -g -o myprogram myprogram.c
# Run under gdb
gdb ./myprogram
(gdb) run
# When it crashes:
Program received signal SIGSEGV, Segmentation fault.
0x0000... in main () at myprogram.c:15
# See the exact line and call stack
(gdb) backtrace # shows the call stack
(gdb) print ptr # inspect the bad pointer
(gdb) frame 1 # move up the stack
(gdb) list # show surrounding code
Encontrando Bugs de Memória com Valgrind
# Valgrind catches invalid access, leaks, and use-after-free
valgrind --leak-check=full ./myprogram
# Output pinpoints the problem:
# Invalid write of size 4
# at 0x... in main (myprogram.c:15)
# Address 0x0 is not stack'd, malloc'd or (recently) free'd
# It shows exactly where and what kind of memory error occurred
Usando AddressSanitizer (rápido, integrado)
# Compile with AddressSanitizer — catches many bugs at runtime
gcc -fsanitize=address -g -o myprogram myprogram.c
./myprogram
# ASan reports:
# ERROR: AddressSanitizer: heap-buffer-overflow
# WRITE of size 4 at 0x...
# #0 in main myprogram.c:15
# Much faster than Valgrind and often clearer output
Perguntas Frequentes
P: O que significa “core dumped”?
R: Quando o programa trava, o sistema operacional grava um “core dump” – um instantâneo da memória do programa no momento da falha. Você pode carregá-lo em gdb (gdb ./program core) para inspecionar o estado no momento da falha.
P: Por que meu programa trava às vezes, mas nem sempre?
R: Comportamentos indefinidos (como usar memória não inicializada ou um leve estouro de buffer) nem sempre podem desencadear uma falha visível — depende do que está na memória. Isso torna esses bugs perigosos; use ASan/Valgrind para capturá-los de forma confiável.
P: gdb vs Valgrind vs AddressSanitizer – qual devo usar?
R: AddressSanitizer (sinalizador de compilação) é mais rápido e detecta a maioria dos bugs de memória — comece aqui. Valgrind é minucioso quanto a vazamentos e não precisa de recompilação. gdb serve para inspecionar interativamente uma falha específica. Freqüentemente você usa primeiro o ASan e depois o gdb para se aprofundar.
P: Em primeiro lugar, como posso evitar falhas de segurança?
R: Inicialize ponteiros, verifique NULL antes de desreferenciar, respeite os limites do array, defina ponteiros como NULL após a liberação e use ponteiros inteligentes C++ modernos (unique_ptr, shared_ptr) que gerenciam a memória automaticamente.
P: Devo usar ponteiros brutos em C++ moderno?
R: Prefira ponteiros e contêineres inteligentes (vetor, string) que lidam com a memória com segurança e eliminam a maioria das causas de falha de segmento. Ponteiros brutos ainda são úteis para referências que não são proprietárias, mas evite novas/exclusões manuais onde os tipos RAII funcionam.
Conclusão
Uma falha de segmentação significa acesso à memória inválido – geralmente um ponteiro NULL/pendente, estouro de buffer ou estouro de pilha. O fluxo de trabalho de depuração:compilar com-fsanitize=address -g, execute-o (AddressSanitizer identifica a linha exata e o tipo de bug) ou use obacktracedo gdb para ver o local do acidente
🔗 Share this article
✍️ Leave a Comment