Publicidade

Aula 34: Erros em Execução e Segfaults

Causas de segmentation faults em C e como corrigi-los — seu guia completo de erros em tempo de execução.

📖 O que você vai aprender nesta página
✅ Essenciais que você precisa saber
  • Um segmentation fault significa acessar memória proibida
  • Desreferenciar NULL, acesso fora dos limites e usar antes de inicializar são as principais causas
  • Divisão por zero e stack overflow
⭐ Leia se tiver tempo
  • Os perigos do comportamento indefinido
  • Detecção com valgrind e sanitizers
  • Core dumps e depuração post-mortem com gdb

O que são Erros em Tempo de Execução — Compilou, mas...

Uma compilação bem-sucedida só significa que a sintaxe está correta. Um erro em tempo de execução acontece quando o programa trava durante a execução. O compilador não consegue detectá-los, o que os torna mais difíceis de depurar.
Segmentation Fault
Ler ou escrever em memória proibida. O mais comum dos erros em tempo de execução.
Bus Error
Violações de alinhamento de memória. Se ocorrem, depende da plataforma.
Floating Point Exception
Divisão por zero (x / 0).
Laço infinito / travamento
O programa nunca termina. Encerre com Ctrl+C.
Abordagem geral: (1) use printf para descobrir até onde a execução chega, (2) imprima os valores das variáveis suspeitas e (3) verifique com cuidado índices de arrays e ponteiros.

Segmentation Fault — Principais Causas

Segmentation fault (core dumped) é o erro que iniciantes mais encontram. Significa "você tocou em memória que não tinha permissão para acessar."

Top 5 causas

1
Acesso a array fora dos limites
int a[5];
a[10] = 99;  // só existem a[0]..a[4]; escrever a[10] = crash imediato

// Erro comum: limite de laço errado
for (int i = 0; i <= 5; i++)  // acessa a[5] quando i=5 -> Errado
  a[i] = i;
2
Desreferenciar um ponteiro NULL
int *p = NULL;
*p = 10;  // escrevendo no endereço para o qual NULL aponta -> Segfault

// Correção: verifique NULL antes de usar
if (p != NULL) { *p = 10; }
3
& ausente em scanf
int x;
scanf("%d", x);   // Errado: usa o "valor" de x como endereço -> crash
scanf("%d", &x);  // OK: passa o "endereço" de x
4
Usar um ponteiro não inicializado
int *p;     // não inicializado -> contém endereço de lixo
*p = 42;    // escrevendo em endereço de lixo -> Segfault

// Correção: sempre inicialize, ou aloque com malloc antes de usar
5
Stack overflow (recursão infinita)
int fact(int n) {
  return n * fact(n - 1); // sem caso base -> recursão infinita -> stack overflow
}
// Correção: adicione if (n <= 1) return 1;

Erros Relacionados a Memória — Minas Escondidas

Bugs de memória podem aparecer como resultados errados sem nunca disparar um segfault.
Buffer overrun
Escrever além do fim de um array corrompe a memória vizinha.
char name[5];
strcpy(name, "HelloWorld"); // só 5 bytes mas 10+NUL -> overflow

// Correção: strncpy(name, "Hello", sizeof(name)-1);
Vazamento de memória
Esquecer de dar free na memória alocada por malloc deixa memória inalcançável que se acumula ao longo do tempo.
int *p = malloc(sizeof(int) * 100);
// ... usa p ...
// esqueceu free(p); -> vazamento de memória
Dangling pointer / double free
Usar um ponteiro depois de liberado leva a comportamento imprevisível.
free(p);
*p = 10;   // escrevendo em memória liberada -> comportamento indefinido
free(p);   // double free -> possível crash

// Correção: defina p = NULL; após free

Erros de Lógica — Roda, Mas a Resposta é Errada

Sem crash, mas o resultado está errado. Esses são os bugs mais difíceis de rastrear — depuração com printf é seu maior aliado.
Armadilha da divisão inteira
int a = 7, b = 2;
double avg = a / b;     // -> 3.000000 (não 3.5!)
double avg = (double)a / b; // -> 3.500000 (OK)
Inteiro dividido por inteiro descarta a parte fracionária. Faça cast de um dos lados para double.
Precedência de operadores
if (x & 1 == 0)   // é lido como x & (1==0) — não é o que você quis!
if ((x & 1) == 0) // OK: parênteses explícitos
Precedência de bit a bit vs. comparação é contra-intuitiva. Na dúvida, coloque parênteses.
break ausente no switch (fall-through)
switch (n) {
  case 1: printf("one\n");  // sem break -> case 2 e 3 também rodam
  case 2: printf("two\n");
  case 3: printf("three\n");
}
Ponto e vírgula solto depois do if
if (x > 0);  // ← este ; encerra a instrução if
{
  printf("positive\n"); // ← sempre executa!
}

Checklist de Solução de Problemas

Quando encontrar um erro, percorra este checklist de cima para baixo.

Se vir um Segmentation Fault

Quando os resultados estão errados

Quando há muitos erros de compilação

Publicidade
← Aula anterior
Aula 33: Erros de Compilação

Aulas relacionadas

Referência
Aula 33: Dicionário de Erros de Compilação
Catálogo de erros de compilação em C e como corrigi-los.
Laços, Arrays, Strings
Aula 17: Técnicas de Depuração
Como depurar programas em C.
Avançado
Aula 27: Noções de Ponteiro
Entenda ponteiros em C com visualização de memória.

FAQ

P. Qual a diferença entre erros de compilação e erros em tempo de execução?

R. Erros de compilação são problemas de sintaxe detectados durante a compilação; nenhum executável é produzido. Erros em tempo de execução acontecem enquanto o programa está rodando — o código compila, mas o programa trava. Erros em tempo de execução são mais difíceis de detectar.

P. Peguei um segmentation fault. Qual a causa?

R. Segmentation faults vêm de acesso ilegal a memória. Os principais culpados: (1) desreferenciar um ponteiro NULL, (2) acessar memória liberada, (3) acesso a array fora dos limites e (4) stack overflow. Revise o manuseio de ponteiros e memória.

P. Como eu depuro um laço infinito?

R. (1) Verifique a condição do laço e o critério de saída. (2) Use printf() nas variáveis do laço para ver onde elas divergem do esperado. (3) Um depurador passo a passo também é eficaz. A maioria dos laços infinitos vem de condições ruins ou incremento ausente.

P. Como encontrar corrupção de memória?

R. Corrupção de memória é complicada porque os sintomas frequentemente aparecem longe da causa real. Ferramentas eficazes incluem (1) valgrind (Linux/Mac), (2) sanitizers como UBSan e ASan e (3) revisão cuidadosa do uso de arrays e ponteiros.

Quiz

Confira seu entendimento.

Q1. Qual a principal causa de segmentation faults?

Erros de sintaxe
Acesso ilegal a memória
Nomes de variáveis errados

Causas comuns incluem desreferenciar ponteiros NULL, acesso a array fora dos limites e uso de memória liberada. O compilador não detecta nenhum deles.

Q2. O que acontece quando você divide por 0?

Erro de compilação
Erro em tempo de execução (divisão por zero)
O resultado é 0

Divisão inteira por 0 é um erro em tempo de execução. Se estiver dividindo por uma variável, verifique primeiro se ela não é zero.

Q3. Como corrigir um laço infinito?

Mudar as configurações do compilador
Revisar a condição de saída do laço
Mudar o tipo da variável

Laços infinitos acontecem quando a condição de saída nunca é atendida. Verifique se há uma atualização de contador ausente ou lógica da condição com falhas.

Compartilhe este artigo
Compartilhar no X Compartilhar no Facebook