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.

Você vai aprender
  • As partes de uma mensagem: arquivo:linha:coluna: error: descrição, a linha copiada e o ^.
  • A diferença entre error, warning, note e fatal error.
  • As palavras em inglês que mais aparecem, e um método para consertar sem se perder.
  • Os erros mais comuns: ; faltando, #include faltando 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 de printf".

E as outras linhas:

Linha da mensagemO 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

TipoQuer dizerO 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êsEm portuguêsExemplo
expectedesperava (e não achou)expected ‘;’
before / afterantes de / depois debefore ‘}’ token
token"pedaço": cada palavra ou símbolo do código‘}’ token
undeclarednão declarado: um nome que ninguém criou‘idadi’ undeclared
first use in this functionprimeiro uso nesta funçãoidem
undeclared identifiernome não declarado (forma do Clang)use of undeclared identifier 'x'
implicit declaration of functionuma função usada sem ter sido apresentadaimplicit declaration of function ‘printf’
did you meanvocê quis dizerdid you mean ‘idade’?
missingfaltandomissing terminating " character
terminatingde fechamentoidem
at end of inputno fim do arquivoexpected declaration or statement at end of input
statement / declarationinstrução / declaração (criação de algo)idem
to match thispara fazer par com estenote: to match this ‘(’
makes integer from pointertransforma endereço (ponteiro) em número: tipo erradoinitialization of ‘int’ from ‘char *’ makes integer from pointer
strayperdido, soltostray ‘\342’ in program
No such file or directoryarquivo ou diretório inexistentefatal error: studio.h: …
In file included fromno arquivo incluído a partir deo caminho até um cabeçalho da biblioteca
undefined referencereferência não definida (erro do ligador, seção 6)undefined reference to `main'

3. O método: um erro de cada vez

  1. Leia só o primeiro error:. Os seguintes podem ser consequência dele.
  2. Vá até a linha e a coluna indicadas.
  3. Leia a descrição com o vocabulário acima. Se houver um did you mean ou uma sugestão desenhada embaixo da linha, considere-a (mas não obedeça cegamente).
  4. Se a linha parece certa, olhe a de cima. Ponto e vírgula, aspas e chaves esquecidos são percebidos só depois.
  5. Corrija só esse erro e compile de novo. Muitas vezes os outros somem junto.
Os editores mostram os erros enquanto você digita

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’?: "idadi não declarada (primeiro uso nesta função); você quis dizer idade?". A note só 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 #include faltando (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ção main, por onde todo programa começa. Ela não existe.
  • O caminho comprido (Scrt1.o, _start) é um pedacinho da biblioteca que chama a sua main. 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 gcc nã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
No editor do guia, este erro não aparece

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çãoErro de ligação
Quem reclamaO compilador (gcc)O ligador (ld, chamado pelo collect2)
Como começaarquivo.c:linha:coluna: error:/usr/bin/…ld…: …: na função "…" (in function)
Frase típicaexpected, 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çoSintaxe, nomes, tiposmain 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

TipoQuando apareceO programa é criado?Aula
error: de compilaçãoAo compilarNãoEsta
Erro de ligaçãoAo compilar, no fimNãoEsta
warning:Ao compilarSim, mas provavelmente com um enganoAvisos
Erro em tempo de execuçãoAo rodar: o programa cai (Falha de segmentação) ou dá resultado erradoSimComportamento 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

"A linha que o compilador apontou está certa!"

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.

"Consertei, mas o erro continua igual"

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.

Só aparece 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).

Dezenas de mensagens com /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çãogcc 15 (terminal)Clang (editor do guia)
Falta ;expected ‘;’ before ‘…’ / expected ‘,’ or ‘;’ before ‘…’expected ';' after expression / at end of declaration
Falta #includeimplicit 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 existefatal error: studio.h: Arquivo ou diretório inexistentefatal error: 'studio.h' file not found
Aspas abertasmissing terminating " charactermissing terminating '"' character + expected expression
Falta }expected declaration or statement at end of inputexpected '}' + note: to match this '{'
Sobra }expected identifier or ‘(’ before ‘}’ tokenextraneous closing brace ('}')
Nome errado‘x’ undeclared (first use in this function)use of undeclared identifier 'x'
Função com nome erradoimplicit declaration of function ‘pritnf’; did you mean ‘printf’?use of undeclared identifier 'pritnf'; did you mean 'printf'?
Texto num intinitialization of ‘int’ from ‘char *’ makes integer from pointer without a castincompatible pointer to integer conversion initializing 'int' …
Aspas curvasstray ‘\342’ in programunexpected character <U+201C>
Falta a mainreferê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?

Linha:

2. E o erro de verdade?

Na mesma mensagem, o ; provavelmente falta no fim de qual linha?

Linha:

3. Só o primeiro

Complete a opção que faz o gcc parar no primeiro erro:

gcc -std=c23 -Wall -Wextra jogo.c -o jogo

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.
  • error e fatal error impedem o programa; warning não impede; note explica 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=1 mostra 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 com collect2: 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.