C Compilar com gcc
Na aula anterior você compilou o Olá, mundo com o comando mínimo, gcc ola.c -o ola. Ele funciona, mas deixa de lado duas ajudas valiosas: escolher a versão do C e pedir ao compilador que avise quando o código tem cara de erro. Nesta aula você conhece, uma de cada vez, as opções do gcc que vai usar no dia a dia, e monta o comando que o guia usa em todas as aulas daqui para a frente.
- O comando padrão do guia:
gcc -std=c23 -Wall -Wextra arquivo.c -o programa, parte por parte. -o: o nome do programa, e oa.outquando ele falta.-std=c23: escolher a versão do C, e o que muda com-std=c17.-Walle-Wextra: ligar os avisos.- Compilar e rodar numa linha só, com
&&, e por que é preciso recompilar depois de cada mudança. -g(para depurar),-O2(para otimizar) e-c(só compilar, sem montar o programa).- O
clang, o outro compilador de C, que é o do editor do guia.
Os comandos foram executados na máquina fictícia da Ana, com o gcc 15.2 e o clang 21.1 do Ubuntu 26.04, na pasta ~/c. As saídas foram copiadas da tela.
A ideia: os botões do forno
Na aula anterior, o compilador era a cozinheira que transforma a receita (o código) em bolo (o programa). Agora pense no forno que ela usa. Dá para assar um bolo só ligando o forno, mas os botões fazem diferença: a temperatura, o timer, a luz para espiar lá dentro. As opções do gcc são esses botões. Cada uma começa com um hífen (-) e muda um detalhe do trabalho:
| Opção | Botão do forno | O que faz |
|---|---|---|
-o nome | A etiqueta na forma | Dá nome ao programa |
-std=c23 | Qual livro de receitas seguir | Escolhe a versão do C |
-Wall -Wextra | O alarme de fumaça | Liga os avisos sobre código suspeito |
-g | A luz para espiar o forno | Guarda informações para depurar |
-O2 | O modo turbo | Otimiza: o programa fica mais rápido |
-c | Preparar a massa sem assar | Só compila, sem montar o programa |
1. O comando padrão do guia
De agora em diante, todo programa do guia é compilado assim:
$ gcc -std=c23 -Wall -Wextra ola.c -o ola $ ./ola Olá, mundo!
- gcc
- O compilador de C do projeto GNU.
- -std=c23
- standard = "padrão": compile seguindo o C23, a versão mais nova do C (seção 3).
- -Wall
- Warnings: all, "avisos: todos" (o nome engana: são os principais, não todos). Liga os avisos sobre código que compila, mas provavelmente está errado (seção 4).
- -Wextra
- "Avisos extras": mais um grupo de avisos, além dos do
-Wall. - ola.c
- O arquivo de código.
- -o ola
- O nome do programa que vai ser criado (seção 2).
Parece muita coisa para digitar, mas logo vira costume, e a seta para cima ↑ do terminal repete o último comando. O editor do guia usa exatamente essas opções, com o Clang no lugar do gcc: clang -std=c23 -Wall -Wextra programa.c -o programa.
O gcc aceita as opções em qualquer ordem: gcc -o ola ola.c -std=c23 -Wextra -Wall faz o mesmo que o comando acima. O que precisa ficar junto é o -o e o nome que vem logo depois dele. O guia usa sempre a mesma ordem para facilitar a leitura: opções, arquivo, -o e nome.
2. -o: o nome do programa
Você já conhece o -o (de output, "saída") da aula anterior. Recapitulando:
gcc … ola.c -o olacria o programaola.- Sem
-o, o programa se chamaa.out, e cada compilação sem-osobrescreve oa.outanterior. - O nome depois do
-oé o do programa, nunca o do arquivo.c. Ogccse recusa a apagar o seu código (input file ‘ola.c’ is the same as output file, veja Olá, Mundo).
$ gcc ola.c $ ls a.out ola ola.c « sem -o: a.out
E se você escrever o -o mas esquecer o nome?
$ gcc -std=c23 -Wall -Wextra recado.c -o gcc: error: missing filename after ‘-o’
"Falta o nome do arquivo depois do -o". Nada foi compilado.
3. -std=c23: qual C usar
O C existe desde os anos 1970 e ganhou várias versões oficiais, chamadas de padrões: C89, C99, C11, C17 e C23 (o número é o ano). Cada versão acrescenta recursos novos (a história completa está em Versões do C). A opção -std= escolhe qual delas o compilador segue.
Dá para ver qual versão está valendo: o compilador guarda o número dela num nome especial, __STDC_VERSION__. Não se preocupe com o %ld por enquanto (ele encaixa um número no texto; aula Formatos do printf):
#include <stdio.h>
int main(void) {
printf("%ld\n", __STDC_VERSION__);
return 0;
}
$ gcc versao.c -o versao && ./versao 202311 « sem -std: o gcc 15 já usa o C23 (ano 2023, mês 11) $ gcc -std=c23 versao.c -o versao && ./versao 202311 $ gcc -std=c17 versao.c -o versao && ./versao 201710 $ gcc -std=c11 versao.c -o versao && ./versao 201112 $ gcc -std=c99 versao.c -o versao && ./versao 199901
(O && no meio do comando quer dizer "se compilar, rode". Ele é explicado na seção 5.)
Repare na primeira linha: sem -std, o gcc 15 já usa o C23. Então por que escrever -std=c23? Por três motivos:
- Outros compiladores têm outro padrão. Um gcc mais antigo (o 13, por exemplo) usa o C17 quando você não diz nada. Com
-std=c23, o seu comando faz a mesma coisa em qualquer computador. - Sem
-std, o gcc usa o "C23 com extras do GNU" (chamadognu23), que aceita algumas coisas que não são C padrão. Com-std=c23, ele fica mais perto do C oficial. - Fica escrito qual C o programa usa. Quem ler o comando sabe.
O que muda entre as versões
Um exemplo concreto. No C23, bool, true e false (o tipo "verdadeiro ou falso", aula Booleanos) fazem parte da linguagem. Nas versões anteriores, era preciso incluir o cabeçalho stdbool.h:
#include <stdio.h>
int main(void) {
bool ligado = true;
if (ligado)
printf("A luz está acesa.\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra ligado.c -o ligado && ./ligado
A luz está acesa.
$ gcc -std=c17 -Wall -Wextra ligado.c -o ligado
ligado.c: In function ‘main’:
ligado.c:4:5: error: unknown type name ‘bool’
4 | bool ligado = true;
| ^~~~
ligado.c:2:1: note: ‘bool’ is defined in header ‘<stdbool.h>’; this is probably fixable by adding ‘#include <stdbool.h>’
1 | #include <stdio.h>
+++ |+#include <stdbool.h>
2 |
ligado.c:4:19: error: ‘true’ undeclared (first use in this function)
4 | bool ligado = true;
| ^~~~
ligado.c:4:19: note: ‘true’ is defined in header ‘<stdbool.h>’; this is probably fixable by adding ‘#include <stdbool.h>’
ligado.c:4:19: note: each undeclared identifier is reported only once for each function it appears in
Com -std=c17, o mesmo arquivo não compila: "nome de tipo desconhecido: bool". E a nota já ensina o conserto: this is probably fixable by adding ‘#include <stdbool.h>’, "provavelmente se resolve acrescentando o #include <stdbool.h>". Muito código e muitos cursos ainda usam o C99 ou o C11, e por isso o guia sempre mostra o jeito antigo quando ele existe.
No editor abaixo, a caixa foi configurada para compilar com -std=c17 (veja a primeira linha da saída).
Missão: rode e leia o erro. Depois faça o programa compilar no C17 sem mudar a linha do bool: acrescente o cabeçalho que a mensagem do compilador sugere.
#include <stdio.h>
int main(void) {
bool ligado = true;
if (ligado)
printf("A luz está acesa.\n");
return 0;
}
#include <stdio.h>
#include <stdbool.h>
int main(void) {
bool ligado = true;
if (ligado)
printf("A luz está acesa.\n");
return 0;
}
Repare que o Clang do editor diz o problema de outro jeito: error: use of undeclared identifier 'bool' ("uso de identificador não declarado"), sem a sugestão do cabeçalho. Os dois compiladores concordam no essencial: no C17, bool não existe sem o stdbool.h.
4. -Wall e -Wextra: peça para ser avisado
O compilador separa os problemas em dois tipos:
- Erro (
error:): o código está fora das regras do C. O programa não é criado. - Aviso (
warning:): o código segue as regras, mas tem cara de engano. O programa é criado, e o compilador só chama a sua atenção.
A maioria dos avisos vem desligada. Veja este programa, em que a Ana guardou a idade e esqueceu de usá-la:
#include <stdio.h>
int main(void) {
int idade = 29;
printf("Olá, Ana!\n");
return 0;
}
(A linha int idade = 29; cria uma variável, uma caixinha com nome que guarda um número. Elas têm aula própria: Variáveis.)
$ gcc idade.c -o idade
$ « silêncio: compilou sem dizer nada
$ gcc -Wall idade.c -o idade
idade.c: In function ‘main’:
idade.c:4:9: warning: unused variable ‘idade’ [-Wunused-variable]
4 | int idade = 29;
| ^~~~~
Com -Wall, o gcc avisa: unused variable ‘idade’, "variável idade não usada". Aqui é inofensivo, mas muitas vezes uma variável esquecida é sinal de que você queria mostrá-la e se esqueceu. No fim da mensagem, [-Wunused-variable] é o nome do aviso, que o -Wall ligou.
O -Wextra liga mais avisos. Neste programa, a Ana tirou nota 5 e mesmo assim aparece "Aprovada!". Repare no ; perdido no fim da linha do if ("se"; aula if e else):
#include <stdio.h>
int main(void) {
int nota = 5;
if (nota >= 7);
printf("Aprovada!\n");
return 0;
}
$ gcc -Wall nota.c -o nota
nota.c: In function ‘main’:
nota.c:5:5: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
5 | if (nota >= 7);
| ^~
nota.c:6:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
6 | printf("Aprovada!\n");
| ^~~~~~
$ gcc -Wall -Wextra nota.c -o nota
nota.c: In function ‘main’:
nota.c:5:19: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
5 | if (nota >= 7);
| ^
nota.c:5:5: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
5 | if (nota >= 7);
| ^~
nota.c:6:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
6 | printf("Aprovada!\n");
| ^~~~~~
$ ./nota
Aprovada! « o programa foi criado e roda... errado
- O
-Wallpercebeu que o recuo engana: "esteifnão protege… esta instrução, mas ela está recuada como se estivesse protegida". - O
-Wextraacrescentou um aviso mais direto: "sugiro chaves em volta do corpo vazio doif". O;logo depois doifé um "corpo vazio": oiftermina ali, e oprintfroda sempre.
Sem as duas opções, o gcc não diria nada, e a Ana ficaria procurando o erro sozinha. É por isso que o guia sempre compila com -Wall -Wextra, e você deve fazer o mesmo: os avisos são o compilador ajudando você de graça. Os avisos mais comuns, e o -Werror (que transforma avisos em erros), estão na aula Avisos (-Wall -Wextra).
Missão: o editor compila com -Wall -Wextra. Rode e leia o aviso. Depois resolva: o programa tem de mostrar Olá, Ana! e compilar sem nenhum aviso. (O jeito mais simples é apagar a variável que ninguém usa.)
#include <stdio.h>
int main(void) {
int idade = 29;
printf("Olá, Ana!\n");
return 0;
}
#include <stdio.h>
int main(void) {
printf("Olá, Ana!\n");
return 0;
}
5. Compilar e rodar numa linha: &&
Compilar e depois rodar são dois comandos. Para não digitar duas vezes, junte-os com && ("e então"):
$ gcc -std=c23 -Wall -Wextra recado.c -o recado && ./recado Reunião às 14h.
O && é do terminal, não do C: ele roda o segundo comando só se o primeiro deu certo (código de saída 0, lembra do echo $??). Se a compilação falhar, o programa não roda:
$ cat recado.c
#include <stdio.h>
int main(void) {
printf("Reunião às 16h.\n")
return 0;
}
$ gcc -std=c23 -Wall -Wextra recado.c -o recado && ./recado
recado.c: In function ‘main’:
recado.c:4:32: error: expected ‘;’ before ‘return’
4 | printf("Reunião às 16h.\n")
| ^
| ;
5 | return 0;
| ~~~~~~
$ « o ./recado nem foi tentado
Isso protege você de uma armadilha, que a próxima seção mostra.
6. Mudou o código? Compile de novo
O programa é o bolo, não a receita. Mudar o arquivo .c não muda o programa que já existe. Veja a Ana trocando o horário da reunião de 10h para 14h:
$ gcc -std=c23 -Wall -Wextra recado.c -o recado
$ ./recado
Reunião às 10h.
$ cat recado.c « a Ana editou o arquivo e trocou 10h por 14h
#include <stdio.h>
int main(void) {
printf("Reunião às 14h.\n");
return 0;
}
$ ./recado
Reunião às 10h. « o programa ainda é o antigo!
$ gcc -std=c23 -Wall -Wextra recado.c -o recado && ./recado
Reunião às 14h. « agora sim
E tem mais: quando a compilação falha, o gcc não cria um programa novo, mas também não apaga o antigo. Depois do erro da seção 5 (o ; que faltou no "16h"), o programa velho continua lá:
$ ./recado Reunião às 14h. « a versão de antes do erro
Quem compila num comando e roda em outro pode não ver o erro, rodar o programa velho e achar que a mudança "não funcionou". Com o &&, isso não acontece. Regra de ouro: mudou, salvou, compilou, rodou. O editor do guia faz isso sozinho: cada clique em Rodar compila de novo.
7. Duas opções para depois: -g e -O2
Você ainda não vai precisar delas no começo, mas vai vê-las em todo lugar.
-g: informações para depurar
-g guarda dentro do programa um "mapa" que liga cada pedaço do programa à linha do código-fonte de onde ele veio. Quem usa esse mapa são as ferramentas que procuram erros, como o gdb (o depurador, aula Depurar com gdb) e o valgrind (aula Valgrind): com ele, elas dizem "o problema está em ola.c, linha 4" em vez de mostrar só endereços de memória. O mapa ocupa espaço:
$ gcc -std=c23 -Wall -Wextra ola.c -o ola $ gcc -std=c23 -Wall -Wextra -g ola.c -o ola-g $ gcc -std=c23 -Wall -Wextra -O2 ola.c -o ola-o2 $ ls -l total 56 -rwxrwxr-x 1 ana ana 15952 out 17 10:31 ola -rw-rw-r-- 1 ana ana 83 out 17 10:31 ola.c -rwxrwxr-x 1 ana ana 17040 out 17 10:31 ola-g « com -g: cerca de 1 KB a mais -rwxrwxr-x 1 ana ana 15960 out 17 10:31 ola-o2 $ ./ola-g Olá, mundo! « roda igualzinho
-O2: otimizar
-O2 (letra O maiúscula, de optimize, e o número 2) pede ao compilador para caprichar: ele reorganiza o programa para rodar mais rápido, sem mudar o resultado. Sem nenhum -O, o gcc traduz o código do jeito mais direto, o que compila mais rápido e facilita a depuração. Num programa que faz muitas contas, a diferença aparece. Este faz um bilhão de contas (você vai entender o for na aula for); o comando time do terminal mede quanto tempo um programa leva:
#include <stdio.h>
int main(void) {
long long soma = 0;
for (long long i = 1; i <= 1000000000; i++) {
soma = soma + i % 7;
}
printf("Soma: %lld\n", soma);
return 0;
}
$ gcc -std=c23 -Wall -Wextra conta.c -o conta $ gcc -std=c23 -Wall -Wextra -O2 conta.c -o conta-rapido $ time ./conta Soma: 3000000003 real 0m4,219s « cerca de 4 segundos user 0m4,218s sys 0m0,001s $ time ./conta-rapido Soma: 3000000003 « o mesmo resultado... real 0m2,076s « ...na metade do tempo user 0m2,074s sys 0m0,001s
Os tempos mudam de um computador para outro, mas a proporção costuma ser parecida. Também existem -O0 (sem otimização, o normal), -O1, -O3 e -Os (programa menor). No dia a dia, use -g enquanto escreve e procura erros, e -O2 na versão final.
8. -c: só compilar, sem montar o programa
Até aqui, o gcc fez tudo de uma vez. Na verdade, ele trabalha em etapas (veja Como o C Vira Programa): primeiro compila o código, criando um arquivo objeto (.o), e depois o ligador junta esse objeto com a biblioteca padrão e monta o programa. A opção -c manda parar no meio do caminho:
$ gcc -std=c23 -Wall -Wextra -c ola.c $ ls ola* ola ola.c ola.o « apareceu o ola.o $ gcc -std=c23 -Wall -Wextra ola.o -o ola « segunda etapa: ligar $ ./ola Olá, mundo!
- Com
-c, o nome do objeto é o do arquivo, trocando.cpor.o. - O
ola.ojá tem o código traduzido para a linguagem do processador, mas ainda não é um programa: falta a parte que o ligador acrescenta, como o próprioprintf.
Se tentar rodar o objeto, o sistema recusa:
$ ./ola.o bash: ./ola.o: Permissão negada
Para que serve isso? Em programas grandes, divididos em muitos arquivos .c, você compila cada um separadamente e só recompila o que mudou. Esse é o assunto das aulas Vários Arquivos e Makefile. Por enquanto, guarde só que -c cria um .o.
9. A alternativa: clang
O gcc não é o único compilador de C. O Clang (do projeto LLVM) é o outro grande compilador gratuito, padrão no macOS e no Android, e é o que roda no editor do guia. Ele aceita as mesmas opções:
$ clang --version Ubuntu clang version 21.1.8 (6ubuntu1) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/lib/llvm-21/bin $ clang -std=c23 -Wall -Wextra ola.c -o ola-clang $ ./ola-clang Olá, mundo!
A diferença está no texto das mensagens. O mesmo aviso do idade.c nos dois:
$ gcc -std=c23 -Wall -Wextra idade.c -o idade
idade.c: In function ‘main’:
idade.c:4:9: warning: unused variable ‘idade’ [-Wunused-variable]
4 | int idade = 29;
| ^~~~~
$ clang -std=c23 -Wall -Wextra idade.c -o idade
idade.c:4:9: warning: unused variable 'idade' [-Wunused-variable]
4 | int idade = 29;
| ^~~~~
1 warning generated.
- O
gcccomeça dizendo em qual função está (In function ‘main’) e usa aspas tortas (‘idade’). - O Clang usa aspas retas (
'idade') e termina com um placar:1 warning generated("1 aviso gerado").
Neste aviso as palavras coincidiram; em muitos outros, não. A aula Lendo Erros do Compilador mostra os dois lado a lado. Instalar o Clang no Ubuntu: sudo apt install clang.
Formas de compilar, e qual usar
| Comando | Quando usar |
|---|---|
gcc -std=c23 -Wall -Wextra ola.c -o ola | Sempre, nas aulas do guia e nos seus programas |
gcc -std=c23 -Wall -Wextra ola.c -o ola && ./ola | Para compilar e já testar, sem risco de rodar o programa velho |
gcc -std=c23 -Wall -Wextra -g ola.c -o ola | Quando for usar o gdb ou o valgrind |
gcc -std=c23 -Wall -Wextra -O2 ola.c -o ola | Na versão final de um programa que precisa ser rápido |
gcc -std=c17 … ou -std=c99 … | Quando o projeto (ou o professor) exigir uma versão mais antiga |
clang -std=c23 -Wall -Wextra ola.c -o ola | Para comparar mensagens, ou num computador que só tem o Clang (macOS) |
gcc ola.c -o ola | Só para um teste rápido: sem avisos, você perde ajuda |
Quando dá errado
unrecognized command-line option ‘-wall’
$ gcc -wall ola.c -o ola gcc: error: unrecognized command-line option ‘-wall’; did you mean ‘-Wall’?
O que significa: "opção de linha de comando não reconhecida". As opções diferenciam maiúsculas de minúsculas: o certo é -Wall, com W maiúsculo. O gcc até sugere: did you mean, "você quis dizer".
Como consertar: -Wall e -Wextra, com W maiúsculo. O mesmo vale para -O2 (letra O maiúscula, não zero).
unrecognized command-line option ‘-std=c24’
$ gcc -std=c24 ola.c -o ola gcc: error: unrecognized command-line option ‘-std=c24’; did you mean ‘-std=c23’?
O que significa: não existe C24. As versões são c89, c99, c11, c17 e c23 (e as variantes gnu89, gnu99… com extras do GNU).
Como consertar: -std=c23.
O que significa: você rodou o programa sem compilar de novo, ou a compilação falhou e você não viu (o programa antigo continua na pasta, seção 6).
Como consertar: compile e rode juntos: gcc -std=c23 -Wall -Wextra recado.c -o recado && ./recado. Se aparecer um error:, o programa não roda e você vê na hora o que corrigir.
bash: ./ola.o: Permissão negada
O que significa: você tentou rodar o arquivo objeto, que é criado pelo -c e não é um programa completo.
Como consertar: ligue o objeto (gcc ola.o -o ola) ou compile sem -c, e rode ./ola.
missing filename after ‘-o’
$ gcc -std=c23 -Wall -Wextra recado.c -o gcc: error: missing filename after ‘-o’
O que significa: o -o precisa do nome do programa logo depois dele.
Como consertar: … recado.c -o recado.
Referência rápida
| Opção | O que faz |
|---|---|
-o nome | Dá nome ao programa (sem ela: a.out) |
-std=c23 | Segue o C23. Outras: c17, c11, c99, c89 (sem -std, o gcc 15 usa gnu23) |
-Wall | Liga os principais avisos |
-Wextra | Liga mais avisos, além dos do -Wall |
-Werror | Trata avisos como erros (aula Avisos) |
-g | Guarda informações para o gdb e o valgrind |
-O2 | Otimiza o programa (também -O0, -O1, -O3, -Os) |
-c | Só compila: cria arquivo.o, sem montar o programa |
cmd1 && cmd2 | (terminal) roda cmd2 só se cmd1 deu certo |
clang … | O outro compilador; aceita as mesmas opções |
O comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.
Exercícios
1. A versão
Complete o comando para compilar seguindo o C23:
2. Os avisos
Complete com as duas opções que ligam os avisos, na ordem do guia:
3. Compilar e rodar
Complete para rodar o jogo só se a compilação der certo:
4. O que acontece quando o código tem um aviso (warning:), mas nenhum erro?
5. Para que serve a opção -g?
6. Você rodou gcc -std=c23 -Wall -Wextra -c conta.c. O que aparece na pasta?
Missão final: o convite da festa
Missão: a Ana escreveu o convite da festa, mas o programa tem um erro e, depois que você corrigir o erro, vai aparecer um aviso. Conserte os dois: o programa tem de mostrar as duas linhas do convite e compilar sem nenhum aviso.
Festa da Ana Sábado, às 19h
#include <stdio.h>
int main(void) {
int convidados = 12;
printf("Festa da Ana\n")
printf("Sábado, às 19h\n");
return 0;
}
#include <stdio.h>
int main(void) {
printf("Festa da Ana\n");
printf("Sábado, às 19h\n");
return 0;
}
Resumo
- O comando padrão do guia é
gcc -std=c23 -Wall -Wextra arquivo.c -o programa. -odá nome ao programa; sem ele, o nome éa.out.-std=c23escolhe a versão do C. O gcc 15 já usa o C23 (com extras do GNU) sem ela, mas escrever deixa o comando igual em qualquer computador. Com-std=c17, por exemplo,boolprecisa dostdbool.h.-Wall -Wextraligam os avisos: o programa é criado mesmo assim, mas o compilador aponta código suspeito. Use sempre.&&roda o programa só se compilou. Mudou o código? Compile de novo: o programa antigo não muda sozinho, e uma compilação que falha não o apaga.-gserve para depurar,-O2para otimizar e-cpara criar só o arquivo objeto.o.- O
clangaceita as mesmas opções e é o compilador do editor do guia; as mensagens dizem o mesmo com outras palavras. - Próximo passo: Sintaxe, as regras de escrita do C.