C Lendo Erros do Compilador
Todo programador, do primeiro dia ao vigésimo ano, vê erros de compilação o tempo todo. A diferença é que quem tem experiência lê a mensagem e sabe onde olhar em segundos. As mensagens do gcc estão em inglês e às vezes parecem enormes, mas seguem sempre o mesmo formato e usam um vocabulário pequeno. Nesta aula você aprende a ler esse formato, conhece os erros mais comuns de quem começa, com a mensagem real e o conserto, e descobre por que um erro às vezes aparece na linha errada, ou vira dezenas de linhas.
- As partes de uma mensagem:
arquivo:linha:coluna: error: descrição, a linha copiada e o^. - A diferença entre
error,warning,noteefatal error. - As palavras em inglês que mais aparecem, e um método para consertar sem se perder.
- Os erros mais comuns:
;faltando,#includefaltando ou errado, aspas e chaves, nome errado, tipo errado e as aspas curvas copiadas da internet. - Por que o erro às vezes aparece na linha de baixo, e por que um erro só gera muitas mensagens.
- A diferença entre erro de compilação e erro de ligação (
undefined reference). - As mesmas mensagens no Clang, o compilador do editor do guia.
Todos os códigos desta aula têm erros de propósito. Eles foram compilados na máquina fictícia da Ana com gcc -std=c23 -Wall -Wextra (gcc 15.2), numa pasta ~/c/erros, e as mensagens do Clang são as do editor do guia. Tudo foi copiado sem mudar nada, inclusive as aspas tipográficas ‘assim’ que o gcc usa.
A ideia: o corretor ortográfico
Quando você escreve no celular, o corretor sublinha uma palavra em vermelho e sugere outra. Ele não entende o que você quis dizer: só sabe que aquela palavra não está no dicionário dele. Às vezes a sugestão é perfeita; às vezes é engraçada.
O compilador é um corretor muito mais rígido. Ele lê o seu código do começo ao fim e, quando algo não se encaixa nas regras do C, anota onde percebeu o problema e o que esperava encontrar. Duas coisas para guardar desde já:
- Um erro de compilação não estraga nada. O programa só não é criado até você consertar. Pode errar à vontade.
- O compilador diz onde percebeu o erro, e isso nem sempre é onde você errou. Você vai ver por quê.
1. Anatomia de uma mensagem
A Ana esqueceu o ponto e vírgula no fim da linha 4:
#include <stdio.h>
int main(void) {
printf("Lista de compras\n")
printf("Arroz\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra pv.c -o pv
pv.c: In function ‘main’:
pv.c:4:33: error: expected ‘;’ before ‘printf’
4 | printf("Lista de compras\n")
| ^
| ;
5 | printf("Arroz\n");
| ~~~~~~
A linha mais importante é a segunda. Ela tem cinco partes, separadas por dois-pontos:
- pv.c:
- O arquivo onde está o problema. Em programas com vários arquivos (aula Vários Arquivos), isso importa muito.
- 4:
- A linha. Os editores mostram o número das linhas na margem; no VS Code, Ctrl+G pula direto para uma linha (aula Editores e IDEs).
- 33:
- A coluna: em que caractere da linha, contando da esquerda. Na prática, quem mostra a coluna é o
^, logo abaixo. - error:
- O tipo da mensagem (veja a tabela abaixo).
- expected ‘;’ before ‘printf’
- A descrição: "esperava
;antes deprintf".
E as outras linhas:
| Linha da mensagem | O que é |
|---|---|
pv.c: In function ‘main’: | Contexto: "na função main". Diz em qual função estão os erros que vêm a seguir. |
4 | printf("Lista de compras\n") | A linha 4 do seu código, copiada, para você não precisar procurar. |
| ^ | O acento circunflexo aponta a coluna exata: logo depois do ). |
| ; | Uma sugestão de conserto (em inglês, fix-it): o gcc desenha o ; onde ele deveria estar. |
5 | printf… e ~~~~~~ | A linha 5, com o printf sublinhado (~~~~~~): é o "printf" da descrição, a primeira coisa que o gcc viu depois do lugar onde faltou o ;. |
Os tipos de mensagem
| Tipo | Quer dizer | O programa é criado? |
|---|---|---|
error: | Erro: o código está fora das regras do C. | Não |
fatal error: | Erro tão grave que o compilador nem continua lendo (por exemplo, um #include de arquivo que não existe). | Não |
warning: | Aviso: o código segue as regras, mas tem cara de engano (aula Avisos). | Sim |
note: | Nota: uma informação extra sobre a mensagem anterior (onde algo foi declarado, qual parêntese ficou aberto…). | (não conta) |
Depois de um erro, o programa não é criado, e o gcc termina com o código de saída 1, que quer dizer "deu errado" (aula Olá, Mundo). O echo $? mostra esse código:
$ gcc -std=c23 -Wall -Wextra pv.c -o pv; echo "código de saída: $?"
pv.c: In function ‘main’:
pv.c:4:33: error: expected ‘;’ before ‘printf’
4 | printf("Lista de compras\n")
| ^
| ;
5 | printf("Arroz\n");
| ~~~~~~
código de saída: 1
$ ls
aspas.c
cascata.c
chave2.c
chave.c
inc.c
ligacao.c
nome.c
pv2.c
pv.c « só os arquivos .c: nenhum programa "pv" foi criado
tipo.c
No editor do guia, o Clang diz a mesma coisa com outras palavras: expected ';' after expression, "esperava ; depois da expressão", e aponta a mesma coluna. Experimente provocar o erro:
Missão: este programa está certo. Apague o ; do fim da linha 4, rode e leia a mensagem: o arquivo, a linha, a coluna, o error, a descrição e o ; desenhado embaixo. Repare também no [código de saída do clang: 1].
#include <stdio.h>
int main(void) {
printf("Lista de compras\n");
printf("Arroz\n");
return 0;
}
#include <stdio.h>
int main(void) {
printf("Lista de compras\n")
printf("Arroz\n");
return 0;
}
2. O vocabulário das mensagens
Você não precisa saber inglês para ler erros de compilação. Umas vinte palavras resolvem quase tudo:
| Em inglês | Em português | Exemplo |
|---|---|---|
expected | esperava (e não achou) | expected ‘;’ |
before / after | antes de / depois de | before ‘}’ token |
token | "pedaço": cada palavra ou símbolo do código | ‘}’ token |
undeclared | não declarado: um nome que ninguém criou | ‘idadi’ undeclared |
first use in this function | primeiro uso nesta função | idem |
undeclared identifier | nome não declarado (forma do Clang) | use of undeclared identifier 'x' |
implicit declaration of function | uma função usada sem ter sido apresentada | implicit declaration of function ‘printf’ |
did you mean | você quis dizer | did you mean ‘idade’? |
missing | faltando | missing terminating " character |
terminating | de fechamento | idem |
at end of input | no fim do arquivo | expected declaration or statement at end of input |
statement / declaration | instrução / declaração (criação de algo) | idem |
to match this | para fazer par com este | note: to match this ‘(’ |
makes integer from pointer | transforma endereço (ponteiro) em número: tipo errado | initialization of ‘int’ from ‘char *’ makes integer from pointer |
stray | perdido, solto | stray ‘\342’ in program |
No such file or directory | arquivo ou diretório inexistente | fatal error: studio.h: … |
In file included from | no arquivo incluído a partir de | o caminho até um cabeçalho da biblioteca |
undefined reference | referência não definida (erro do ligador, seção 6) | undefined reference to `main' |
3. O método: um erro de cada vez
- Leia só o primeiro
error:. Os seguintes podem ser consequência dele. - Vá até a linha e a coluna indicadas.
- Leia a descrição com o vocabulário acima. Se houver um
did you meanou uma sugestão desenhada embaixo da linha, considere-a (mas não obedeça cegamente). - Se a linha parece certa, olhe a de cima. Ponto e vírgula, aspas e chaves esquecidos são percebidos só depois.
- Corrija só esse erro e compile de novo. Muitas vezes os outros somem junto.
O VS Code (com a extensão de C ou o clangd) sublinha os erros em vermelho, com a mesma mensagem do compilador ao passar o mouse (aula Editores e IDEs). Mesmo assim, saber ler a mensagem do terminal é essencial: ela é a palavra final.
4. Os erros mais comuns de quem começa
4.1. Faltou o ;, e o erro aparece na linha de baixo
No pv.c, o ; faltava na linha 4, e o gcc apontou a linha 4. Mas nem sempre é assim. Aqui o ; falta no fim de uma linha que cria uma variável (variáveis são da aula Variáveis):
#include <stdio.h>
int main(void) {
int idade = 29
printf("A Ana tem %d anos.\n", idade);
return 0;
}
$ gcc -std=c23 -Wall -Wextra pv2.c -o pv2
pv2.c: In function ‘main’:
pv2.c:5:5: error: expected ‘,’ or ‘;’ before ‘printf’
5 | printf("A Ana tem %d anos.\n", idade);
| ^~~~~~
pv2.c:4:9: warning: unused variable ‘idade’ [-Wunused-variable]
4 | int idade = 29
| ^~~~~
O erro está na linha 4, mas o gcc apontou a linha 5, coluna 5. Por quê? Para o compilador, a quebra de linha não significa nada (aula Sintaxe). Ele leu int idade = 29 printf(… como uma coisa só, e só percebeu o problema ao chegar no printf, que não tem como continuar a frase. A descrição ajuda: "esperava , ou ; antes de printf". Antes do printf está o fim da linha 4.
E o aviso de "variável não usada"? É um efeito colateral: como a linha 5 não foi entendida, o compilador acha que a idade nunca foi usada. Consertando o erro, o aviso some.
O Clang, nesse caso, aponta direto para o lugar certo (é por isso que o editor do guia às vezes é mais preciso que o terminal):
$ clang -std=c23 -Wall -Wextra pv2.c -o pv2
pv2.c:4:19: error: expected ';' at end of declaration
4 | int idade = 29
| ^
| ;
1 error generated.
"Esperava ; no fim da declaração". O Clang termina sempre com uma contagem: 1 error generated. ("1 erro gerado").
Conserto: o ; no fim da linha de cima da que foi apontada (no gcc) ou exatamente onde o ^ indica (no Clang).
4.2. Faltou o #include, ou o nome dele está errado
int main(void) {
printf("Olá!\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra inc.c -o inc
inc.c: In function ‘main’:
inc.c:2:5: error: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
2 | printf("Olá!\n");
| ^~~~~~
inc.c:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
+++ |+#include <stdio.h>
1 | int main(void) {
inc.c:2:5: warning: incompatible implicit declaration of built-in function ‘printf’ [-Wbuiltin-declaration-mismatch]
2 | printf("Olá!\n");
| ^~~~~~
inc.c:2:5: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
"Declaração implícita da função printf": você usou o printf sem que ninguém o apresentasse ao compilador. Quem apresenta é o stdio.h. A note é generosa: "inclua <stdio.h> ou forneça uma declaração de printf", e a linha com +++ |+ mostra a linha que ele acrescentaria no topo. No C antigo, isso era só um aviso; no gcc 15, com o C23, é erro. No editor do guia, o Clang diz call to undeclared library function 'printf' ("chamada a uma função da biblioteca não declarada") e dá a mesma dica: include the header <stdio.h>.
E se o #include está lá, mas com o nome errado? studio.h é um engano clássico (o certo é stdio.h, de standard input/output, "entrada e saída padrão"):
$ gcc -std=c23 -Wall -Wextra studio.c -o studio
studio.c:1:10: fatal error: studio.h: Arquivo ou diretório inexistente
1 | #include <studio.h>
| ^~~~~~~~~~
compilation terminated.
Um fatal error: o arquivo studio.h não existe, e o gcc desiste na hora (compilation terminated, "compilação encerrada"). A parte "Arquivo ou diretório inexistente" vem do sistema, por isso sai em português. No Clang: fatal error: 'studio.h' file not found.
Conserto: o #include do cabeçalho certo, escrito certo, no topo do arquivo. Cada função da biblioteca mora num cabeçalho; as aulas sempre dizem qual.
4.3. Aspas que não fecham
$ gcc -std=c23 -Wall -Wextra aspas.c -o aspas « printf("Olá, Ana!\n);
aspas.c: In function ‘main’:
aspas.c:4:12: warning: missing terminating " character
4 | printf("Olá, Ana!\n);
| ^
aspas.c:4:12: error: missing terminating " character
4 | printf("Olá, Ana!\n);
| ^~~~~~~~~~~~~~
aspas.c:5:5: error: expected expression before ‘return’
5 | return 0;
| ^~~~~~
aspas.c:5:14: error: expected ‘;’ before ‘}’ token
5 | return 0;
| ^
| ;
6 | }
| ~
"Falta o caractere " de fechamento". O ^~~~ sublinha o texto que ficou aberto: das aspas de abertura até o fim da linha. O ); foi parar dentro do texto, e por isso vieram os outros dois erros, na linha 5: o compilador chegou ao return ainda esperando o resto do printf. Consertando as aspas, eles somem.
Conserto: feche as aspas: printf("Olá, Ana!\n");. Os editores pintam o texto de uma cor só até as aspas fecharem, o que ajuda a enxergar. As aspas e as barras dentro do texto estão na aula Novas Linhas e Escapes.
4.4. Chave faltando ou sobrando
Faltou a } que fecha a main (o arquivo termina logo depois do return 0;):
$ gcc -std=c23 -Wall -Wextra chave.c -o chave
chave.c: In function ‘main’:
chave.c:5:5: error: expected declaration or statement at end of input
5 | return 0;
| ^~~~~~
"Esperava uma declaração ou instrução no fim da entrada": o arquivo acabou e o bloco da main ainda estava aberto. O Clang é mais claro e mostra o par que ficou sozinho:
$ clang -std=c23 -Wall -Wextra chave.c -o chave
chave.c:5:14: error: expected '}'
5 | return 0;
| ^
chave.c:3:16: note: to match this '{'
3 | int main(void) {
| ^
1 error generated.
"Esperava }", e a note: "para fazer par com esta {". Em programas grandes, a chave que falta pode estar bem longe do fim do arquivo; o recuo bem-feito (aula Sintaxe) é o que permite achá-la. Agora, com uma } a mais na linha 7:
$ gcc -std=c23 -Wall -Wextra chave2.c -o chave2
chave2.c:7:1: error: expected identifier or ‘(’ before ‘}’ token
7 | }
| ^
O gcc diz "esperava um nome ou ( antes de }", porque fora da main ele espera o começo de outra coisa. O Clang é direto: extraneous closing brace ('}'), "chave de fechamento sobrando".
Conserto: toda { tem a sua }. Muitos editores destacam o par quando o cursor está numa chave.
4.5. Nome escrito errado
#include <stdio.h>
int main(void) {
int idade = 29;
printf("A Ana tem %d anos.\n", idadi);
pritnf("Fim\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra nome.c -o nome
nome.c: In function ‘main’:
nome.c:5:36: error: ‘idadi’ undeclared (first use in this function); did you mean ‘idade’?
5 | printf("A Ana tem %d anos.\n", idadi);
| ^~~~~
| idade
nome.c:5:36: note: each undeclared identifier is reported only once for each function it appears in
nome.c:6:5: error: implicit declaration of function ‘pritnf’; did you mean ‘printf’? [-Wimplicit-function-declaration]
6 | pritnf("Fim\n");
| ^~~~~~
| printf
nome.c:4:9: warning: unused variable ‘idade’ [-Wunused-variable]
4 | int idade = 29;
| ^~~~~
Dois erros independentes (aqui, sim, vale a pena ler os dois):
‘idadi’ undeclared (first use in this function); did you mean ‘idade’?: "idadinão declarada (primeiro uso nesta função); você quis dizeridade?". Anotesó avisa que, se o mesmo nome errado aparecer de novo, ele não vai reclamar outra vez.implicit declaration of function ‘pritnf’; did you mean ‘printf’?: o mesmo erro do#includefaltando (4.2), porque, para o compilador,pritnfé uma função que ninguém apresentou.
O aviso de idade não usada é, de novo, consequência: a única linha que usava a idade tinha o nome errado. No Clang: use of undeclared identifier 'idadi'; did you mean 'idade'? e use of undeclared identifier 'pritnf'; did you mean 'printf'?.
Conserto: o nome exato, com as mesmas maiúsculas. Use a sugestão como pista, não como ordem: o compilador sugere o nome mais parecido que ele conhece, que nem sempre é o que você queria.
4.6. Tipo errado
$ gcc -std=c23 -Wall -Wextra tipo.c -o tipo « int idade = "vinte e nove";
tipo.c: In function ‘main’:
tipo.c:4:17: error: initialization of ‘int’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
4 | int idade = "vinte e nove";
| ^~~~~~~~~~~~~~
Uma variável int guarda números inteiros, e a Ana tentou guardar um texto. A frase é comprida: "inicialização de int a partir de char * transforma ponteiro em inteiro sem conversão". char * é o jeito do C chamar o endereço de um texto (aulas Strings e Ponteiros). No Clang: incompatible pointer to integer conversion initializing 'int' with an expression of type 'char[13]' (um texto de 13 bytes: as letras, os dois espaços e a marca de fim). Nomes de tipo assustam no começo; o que importa é perceber "um lado é número, o outro é texto".
Conserto: o valor do tipo certo: int idade = 29;. Tipos são o assunto de Tipos de Dados.
4.7. Aspas curvas copiadas da internet: stray ‘\342’
Copiar código de um PDF, de um site ou de um processador de texto (Word, Google Docs) pode trazer as aspas "bonitas", curvas (“ ”), no lugar das retas ("). Para o compilador, elas são caracteres estranhos:
$ gcc -std=c23 -Wall -Wextra stray.c -o stray « printf(“Olá!\n”);
stray.c: In function ‘main’:
stray.c:4:12: error: stray ‘\342’ in program
4 | printf(<U+201C>Ol<U+00E1>!\n<U+201D>);
| ^~~~~~~~
stray.c:4:13: error: ‘Olá’ undeclared (first use in this function)
4 | printf(“Olá!\n”);
| ^~~
stray.c:4:13: note: each undeclared identifier is reported only once for each function it appears in
stray.c:4:16: error: expected ‘)’ before ‘!’ token
4 | printf(“Olá!\n”);
| ~ ^
| )
stray.c:4:17: error: stray ‘\’ in program
4 | printf(“Olá!\n”);
| ^
stray.c:4:19: error: stray ‘\342’ in program
4 | printf(<U+201C>Ol<U+00E1>!\n<U+201D>);
| ^~~~~~~~
"\342 perdido no programa". O \342 é o primeiro byte da aspa curva em UTF-8 (em octal), e o <U+201C> é o código Unicode dela (aula Acentos e UTF-8). Sem aspas de verdade, o Olá virou um nome não declarado, e o \n ficou solto (stray ‘\’). No Clang, mais direto: unexpected character <U+201C>, "caractere inesperado".
Conserto: troque as aspas curvas pelas retas, digitando-as no editor de código. Se aparecer stray ‘\342’ ou stray ‘\302’, procure aspas curvas, travessões (–) ou espaços especiais vindos de uma cópia.
Missão: este programa tem três erros, de três tipos desta seção. Use o método: rode, conserte só o primeiro erro que aparecer, rode de novo, e repita até ele mostrar as três linhas sem nenhum aviso. (O editor usa o Clang, então as mensagens são as da forma do Clang mostrada acima.)
#include <stdio.h>
int main(void) {
printf("Feira da Ana\n")
int total = 30;
printf("Total: %d\n", totl);
printf("Obrigada!\n);
return 0;
}
#include <stdio.h>
int main(void) {
printf("Feira da Ana\n");
int total = 30;
printf("Total: %d\n", total);
printf("Obrigada!\n");
return 0;
}
5. Um erro, muitas linhas
Às vezes um erro bobo produz uma parede de texto. A Ana esqueceu de fechar as aspas na primeira linha de uma lista:
#include <stdio.h>
int main(void) {
printf("Lista de compras:\n);
printf("Arroz\n");
printf("Feijão\n");
printf("Café\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra cascata.c -o cascata 2>&1 | wc -l 32 « 32 linhas de mensagem $ gcc -std=c23 -Wall -Wextra cascata.c -o cascata 2>&1 | grep -c 'error:' 4 « 4 erros, mas só 1 engano
(O 2>&1 | wc -l conta as linhas da mensagem e o grep -c 'error:' conta as que têm error:; veja a aula Editores e IDEs.) A mensagem completa:
$ gcc -std=c23 -Wall -Wextra cascata.c -o cascata
cascata.c: In function ‘main’:
cascata.c:4:12: warning: missing terminating " character
4 | printf("Lista de compras:\n);
| ^
cascata.c:4:12: error: missing terminating " character « o erro de verdade
4 | printf("Lista de compras:\n);
| ^~~~~~~~~~~~~~~~~~~~~~
cascata.c:5:22: error: expected ‘)’ before ‘;’ token
5 | printf("Arroz\n");
| ^
| )
cascata.c:4:11: note: to match this ‘(’
4 | printf("Lista de compras:\n);
| ^
cascata.c:5:5: error: passing argument 1 of ‘printf’ makes pointer from integer without a cast [-Wint-conversion]
5 | printf("Arroz\n");
| ^~~~~~~~~~~~~~~~~
| |
| int
In file included from cascata.c:1:
/usr/include/stdio.h:370:43: note: expected ‘const char * restrict’ but argument is of type ‘int’
370 | extern int printf (const char *__restrict __format, ...);
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
cascata.c:9:1: warning: format not a string literal and no format arguments [-Wformat-security]
9 | }
| ^
cascata.c:8:14: error: expected ‘;’ before ‘}’ token
8 | return 0;
| ^
| ;
9 | }
| ~
O primeiro erro é o verdadeiro: as aspas da linha 4. Sem elas, o compilador "costurou" a linha 4 com a 5 e tentou entender printf(printf("Arroz\n")…, um printf dentro do outro. Daí vieram as outras reclamações: parêntese sem par, "argumento 1 do printf" do tipo errado, até uma note mostrando a linha 370 do /usr/include/stdio.h, dentro da biblioteca, onde o printf é declarado. Nada disso tem a ver com o seu erro, e você nunca precisa mexer nos arquivos da biblioteca.
O Clang, nesse caso, para no essencial: missing terminating '"' character e expected expression, e só.
Mostrar só o primeiro erro: -fmax-errors=1
A opção -fmax-errors=1 faz o gcc parar no primeiro erro:
$ gcc -std=c23 -Wall -Wextra -fmax-errors=1 cascata.c -o cascata
cascata.c: In function ‘main’:
cascata.c:4:12: warning: missing terminating " character
4 | printf("Lista de compras:\n);
| ^
cascata.c:4:12: error: missing terminating " character
4 | printf("Lista de compras:\n);
| ^~~~~~~~~~~~~~~~~~~~~~
compilation terminated due to -fmax-errors=1.
"Compilação encerrada por causa de -fmax-errors=1". Sem essa opção, role a tela até o topo da mensagem. A regra é sempre a mesma: o primeiro erro primeiro.
6. Erros do ligador: undefined reference
Até aqui, todos os erros foram do compilador, que lê o seu código e confere as regras. Mas fazer um programa tem uma última etapa: a ligação (linking). O ligador (ld, de linker) junta o seu código já compilado com o código da biblioteca e monta o executável (o caminho todo está em Como o C Vira Programa). Ele tem os próprios erros, com outra cara.
Neste programa, a Ana escreveu Main, com M maiúsculo:
#include <stdio.h>
int Main(void) {
printf("Olá!\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra ligacao.c -o ligacao /usr/bin/x86_64-linux-gnu-ld.bfd: /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/Scrt1.o: na função "_start": (.text+0x1b): referência não definida para "main" collect2: error: ld returned 1 exit status
Olhe como é diferente:
- Não há
arquivo:linha:coluna. Para o compilador,Mainé uma função como outra qualquer, e está tudo certo. Quem reclama é o ligador, que trabalha com o código já traduzido e procura a funçãomain, por onde todo programa começa. Ela não existe. - O caminho comprido (
Scrt1.o,_start) é um pedacinho da biblioteca que chama a suamain. O(.text+0x1b)é uma posição dentro do código traduzido. Pode ignorar os dois. - A mensagem está em português! O ligador do Ubuntu tem tradução (o
gccnão tem). Em inglês, que é como você vai encontrá-la em pesquisas, ela é assim:
$ LANG=C gcc -std=c23 -Wall -Wextra ligacao.c -o ligacao /usr/bin/x86_64-linux-gnu-ld.bfd: /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/Scrt1.o: in function `_start': (.text+0x1b): undefined reference to `main' collect2: error: ld returned 1 exit status
(LANG=C na frente do comando pede as mensagens sem tradução.) A última linha, collect2: error: ld returned 1 exit status, só diz "o ligador terminou com erro": a explicação está sempre nas linhas de cima.
Dá para ver as duas etapas separadas. A opção -c manda o gcc só compilar, sem ligar (aula Compilar com gcc): o resultado é um arquivo .o ("objeto"). A compilação passa sem reclamar, e o erro só aparece na ligação:
$ gcc -std=c23 -Wall -Wextra -c ligacao.c $ ls ligacao* ligacao.c ligacao.o « compilou! para o compilador, Main é um nome válido $ gcc ligacao.o -o ligacao /usr/bin/x86_64-linux-gnu-ld.bfd: /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/Scrt1.o: na função "_start": (.text+0x1b): referência não definida para "main" collect2: error: ld returned 1 exit status
A biblioteca C do editor (feita para WebAssembly) não exige a main na ligação: o programa com Main compila sem erro e, ao rodar, cai com Falha de segmentação. Os dois estão dizendo que falta a main, de jeitos diferentes. O outro erro de ligação comum, a função anunciada e nunca escrita, aparece nos dois; veja a comparação em O Editor do Guia.
| Erro de compilação | Erro de ligação | |
|---|---|---|
| Quem reclama | O compilador (gcc) | O ligador (ld, chamado pelo collect2) |
| Como começa | arquivo.c:linha:coluna: error: | /usr/bin/…ld…: …: na função "…" (in function) |
| Frase típica | expected, undeclared, implicit declaration… | referência não definida para (undefined reference to); multiple definition of |
| Última linha | (nenhuma especial) | collect2: error: ld returned 1 exit status |
| Causas comuns no começo | Sintaxe, nomes, tipos | main faltando ou escrita errada (Main); função anunciada e não escrita (aula Protótipos); um .c esquecido no comando (aula Vários Arquivos); -lm esquecido (aula Matemática) |
7. Erro, aviso e erro na hora de rodar
| Tipo | Quando aparece | O programa é criado? | Aula |
|---|---|---|---|
error: de compilação | Ao compilar | Não | Esta |
| Erro de ligação | Ao compilar, no fim | Não | Esta |
warning: | Ao compilar | Sim, mas provavelmente com um engano | Avisos |
| Erro em tempo de execução | Ao rodar: o programa cai (Falha de segmentação) ou dá resultado errado | Sim | Comportamento Indefinido, gdb, Sanitizers |
Os erros de compilação são os melhores erros que existem: aparecem na hora, dizem a linha e não deixam um programa errado chegar a ninguém. Os piores são os que só aparecem ao rodar. É por isso que, na próxima aula, você vai pedir ao compilador que avise o máximo possível.
Quando dá errado
O que significa: o compilador aponta onde percebeu o problema. Um ;, uma aspa ou uma chave esquecidos só são percebidos na linha seguinte, ou até no fim do arquivo (seções 4.1 e 4.4).
Como consertar: leia a descrição (before = "antes de") e olhe a linha de cima. Se nada fizer sentido, compare com a mensagem do Clang, no editor do guia: ele costuma apontar o lugar exato.
O que significa: o arquivo não foi salvo (o compilador lê o disco, não a tela), ou você compilou outro arquivo (um pv.c em vez de pv2.c, outra pasta). Confira o nome do arquivo no começo da mensagem.
Como consertar: salve (Ctrl+S) e confira o comando. A aula Editores e IDEs mostra como ver se o arquivo está salvo.
collect2: error: ld returned 1 exit status
O que significa: essa linha é só o resumo de um erro do ligador. A causa está nas linhas acima dela (referência não definida para …).
Como consertar: role para cima e leia qual nome está faltando (seção 6).
/usr/include/…
O que significa: as note que citam arquivos da biblioteca mostram onde algo foi declarado. Elas são consequência de um erro seu, mais acima (seção 5).
Como consertar: não mexa nos arquivos da biblioteca. Volte ao primeiro error: ou compile com -fmax-errors=1.
fatal error: stdio.h: No such file or directory
O que significa: se o nome está certo e mesmo assim o stdio.h não existe, a instalação do compilador está incompleta: falta a parte de desenvolvimento da biblioteca C.
Como consertar: no Ubuntu e no Debian, sudo apt install build-essential (aula Instalar no Linux).
Referência rápida: gcc × Clang
| Situação | gcc 15 (terminal) | Clang (editor do guia) |
|---|---|---|
Falta ; | expected ‘;’ before ‘…’ / expected ‘,’ or ‘;’ before ‘…’ | expected ';' after expression / at end of declaration |
Falta #include | implicit declaration of function ‘printf’ + note: include ‘<stdio.h>’ | call to undeclared library function 'printf' + note: include the header <stdio.h> |
| Cabeçalho que não existe | fatal error: studio.h: Arquivo ou diretório inexistente | fatal error: 'studio.h' file not found |
| Aspas abertas | missing terminating " character | missing terminating '"' character + expected expression |
Falta } | expected declaration or statement at end of input | expected '}' + note: to match this '{' |
Sobra } | expected identifier or ‘(’ before ‘}’ token | extraneous closing brace ('}') |
| Nome errado | ‘x’ undeclared (first use in this function) | use of undeclared identifier 'x' |
| Função com nome errado | implicit declaration of function ‘pritnf’; did you mean ‘printf’? | use of undeclared identifier 'pritnf'; did you mean 'printf'? |
Texto num int | initialization of ‘int’ from ‘char *’ makes integer from pointer without a cast | incompatible pointer to integer conversion initializing 'int' … |
| Aspas curvas | stray ‘\342’ in program | unexpected character <U+201C> |
Falta a main | referência não definida para "main" (undefined reference to `main') | compila e cai com Falha de segmentação |
Mais mensagens, para consulta: Erros Comuns.
Exercícios
1. Qual linha?
Em jogo.c:12:7: error: expected ‘;’ before ‘return’, em que linha o gcc percebeu o erro?
2. E o erro de verdade?
Na mesma mensagem, o ; provavelmente falta no fim de qual linha?
3. Só o primeiro
Complete a opção que faz o gcc parar no primeiro erro:
4. O gcc mostrou 12 erros. O que fazer primeiro?
5. Qual destas mensagens é de um erro de ligação?
6. O programa da Ana dá stray ‘\342’ in program. Qual é a causa mais provável?
7. O que quer dizer note: numa mensagem do compilador?
Missão final: o conserto completo
Missão: a Ana escreveu este boletim com pressa e o compilador recusou. Há um #include faltando, um tipo errado, um nome errado e uma chave faltando. Use o método: primeiro erro, conserto, compilar de novo. No fim, o programa deve mostrar as três linhas abaixo, sem nenhum aviso:
Boletim Matemática: 9 História: 8
int main(void) {
int matematica = 9;
int historia = "8";
printf("Boletim\n");
printf("Matemática: %d\n", matematica);
printf("História: %d\n", histora);
return 0;
#include <stdio.h>
int main(void) {
int matematica = 9;
int historia = 8;
printf("Boletim\n");
printf("Matemática: %d\n", matematica);
printf("História: %d\n", historia);
return 0;
}
Resumo
- Uma mensagem tem
arquivo:linha:coluna: tipo: descrição, a linha copiada, o^e, às vezes, uma sugestão desenhada embaixo. errorefatal errorimpedem o programa;warningnão impede;noteexplica a mensagem anterior.- O método: leia o primeiro erro, vá à linha, leia a descrição, olhe também a linha de cima, conserte só ele e compile de novo.
- O compilador aponta onde percebeu, não onde você errou:
;, aspas e chaves esquecidos aparecem depois. - Um erro pode gerar dezenas de linhas;
-fmax-errors=1mostra só o primeiro. did you meané um palpite: confira antes de aceitar.- Erros do ligador não têm linha, saem em português no Ubuntu (
referência não definida para; em inglês,undefined reference to) e terminam comcollect2: error: ld returned 1 exit status. - Próximo passo: Avisos (-Wall -Wextra), os problemas que não impedem o programa, mas que você deve levar a sério.