C Pré-processador
Desde o primeiro programa você escreve #include <stdio.h> no topo do arquivo. Essa linha começa com # e não termina com ;. Ela é diferente de todas as outras porque não é para o compilador: é uma ordem para um programa que trabalha antes dele, o pré-processador. Nesta aula você vai ver o que ele faz com o seu arquivo, espiar o resultado com gcc -E e conhecer as primeiras ordens que ele entende: #include, #define, #error e #warning.
- O que é o pré-processador e em que momento ele roda.
- O que
#includefaz de verdade, e a diferença entre<stdio.h>e"loja.h". - Como criar constantes com
#define, e por que ali não vai=nem;. - Como ver o arquivo que o compilador recebe, com
gcc -E. - Os nomes prontos
__FILE__,__LINE__,__func__,__DATE__,__TIME__e__STDC_VERSION__. - Como parar a compilação com
#errore deixar um lembrete com#warningC23.
Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c/prepro, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa (cada opção é explicada em Compilar com gcc). Nas caixas "Experimente", o editor do guia compila de verdade, com o Clang.
A ideia: o assistente que prepara a receita
Imagine uma cozinheira (o compilador) que só sabe seguir uma receita escrita por inteiro, do começo ao fim. Antes de o papel chegar até ela, um assistente passa pela receita e faz trocas mecânicas:
- onde está escrito "cole aqui a receita do molho", ele copia o texto inteiro da receita do molho naquele lugar;
- onde está escrito "XÍCARA", ele troca por "240 ml", porque alguém anotou lá em cima que XÍCARA quer dizer 240 ml;
- ele risca as anotações a lápis na margem, que a cozinheira não precisa ler.
O assistente não sabe cozinhar. Ele não entende a receita: só copia, troca e apaga texto. Esse assistente é o pré-processador. As ordens para ele são as linhas que começam com #, chamadas diretivas (do inglês directive, "instrução"). As anotações a lápis são os seus comentários.
| Etapa | Quem faz | O que recebe | O que entrega |
|---|---|---|---|
| 1. Pré-processamento | o pré-processador | o seu .c, com as linhas # e os comentários | um texto C puro: sem diretivas, sem comentários, com os #include já colados |
| 2. Compilação | o compilador | esse texto | código de máquina (um arquivo .o) |
| 3. Ligação | o ligador (linker) | os .o e as bibliotecas | o programa pronto |
Quando você digita gcc ola.c -o ola, as três etapas rodam em sequência, sem você ver. A aula Como o C Vira Programa mostra o caminho todo. Aqui vamos olhar só a primeira etapa, e o gcc deixa: com a opção -E, ele faz o pré-processamento e para.
1. Ver o que o pré-processador entrega: gcc -E
Comece com um arquivo pequeno, sem #include, para dar para ler o resultado inteiro. A Ana anotou o preço do caderno e o nome da loja com #define:
// Preço do caderno na lojinha da Ana
#define PRECO 12
#define LOJA "Lojinha da Ana"
int main(void) {
int total = PRECO * 3; /* três cadernos */
const char *nome = LOJA;
return total;
}
$ gcc -E mini.c
# 0 "mini.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "mini.c"
int main(void) {
int total = 12 * 3; « PRECO virou 12
const char *nome = "Lojinha da Ana"; « LOJA virou o texto
return total;
}
Compare com o arquivo original. Três coisas aconteceram:
- As duas linhas
#definesumiram: o pré-processador cumpriu a ordem e jogou a linha fora. No lugar ficaram linhas vazias. - Os comentários sumiram, tanto o
//quanto o/* */. O compilador nunca vê os seus comentários. - Onde estava escrito
PRECOagora está12, e onde estavaLOJAestá"Lojinha da Ana". Foi uma troca de texto: nada foi calculado (a conta12 * 3continua lá, para o compilador fazer).
- -E
- "Pare depois do pré-processamento" (E de expand, expandir). O resultado sai na tela (ou num arquivo, com
-o mini.i). Nada é compilado e nenhum programa é criado. # 1 "mini.c"- Marcas de linha. O pré-processador as deixa para o compilador saber de que arquivo e de que linha veio cada pedaço. É graças a elas que um erro consegue dizer
mini.c:6mesmo depois de tudo ter sido trocado. stdc-predef.h- Um arquivo pequeno do sistema que o gcc inclui sozinho em todo programa. Você pode ignorá-lo.
Para ver só o código, sem as marcas e sem as linhas vazias, acrescente -P:
$ gcc -E -P mini.c
int main(void) {
int total = 12 * 3;
const char *nome = "Lojinha da Ana";
return total;
}
gcc -E mostra exatamente o texto que o compilador recebeu. É o "raio X" do pré-processador.
2. #include: colar outro arquivo aqui
Agora o programa de sempre:
#include <stdio.h>
int main(void) {
printf("Olá!\n");
return 0;
}
São 6 linhas. Veja quantas linhas o compilador recebe depois do pré-processamento (o | wc -l conta as linhas da saída, como você viu no Guia Bash):
$ gcc -E ola.c | wc -l 844
Oitocentas e quarenta e quatro linhas! A diretiva #include <stdio.h> mandou o pré-processador procurar o arquivo stdio.h e colar o conteúdo inteiro naquele lugar. E o stdio.h, por sua vez, tem os seus próprios #include, que também foram colados. O fim da saída mostra o seu código lá embaixo, depois de tudo o que veio do stdio.h:
$ gcc -E -P ola.c | tail -8
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
int main(void) {
printf("Olá!\n");
return 0;
}
As linhas com extern são declarações de funções da biblioteca (você aprende a ler isso em Protótipos e static e extern). Entre as 844 linhas está a declaração do printf: é por isso que, sem o #include, o compilador reclama que não conhece o printf.
.h não é a biblioteca. O stdio.h só traz as declarações ("existe uma função printf que recebe isto e devolve aquilo"). O código de verdade do printf já vem pronto, compilado, na biblioteca C do sistema, e entra na etapa de ligação. A extensão .h vem de header, "cabeçalho": o arquivo que vai no alto.
Com < > ou com " "?
Você também pode escrever os seus próprios arquivos .h. A Ana guardou os dados da loja num arquivo separado:
#define LOJA "Lojinha da Ana" #define PRECO_CADERNO 12
#include <stdio.h>
#include "loja.h"
int main(void) {
printf("%s: caderno a R$ %d\n", LOJA, PRECO_CADERNO);
return 0;
}
$ gcc -std=c23 -Wall -Wextra usa_loja.c -o usa_loja $ ./usa_loja Lojinha da Ana: caderno a R$ 12
Repare: o stdio.h está entre < > e o loja.h está entre aspas. A diferença é onde o pré-processador procura:
| Forma | Onde procura | Use para |
|---|---|---|
#include <stdio.h> | Só nas pastas do sistema (/usr/include e companhia) | Arquivos da biblioteca padrão e de bibliotecas instaladas |
#include "loja.h" | Primeiro na pasta do arquivo .c; se não achar, nas pastas do sistema | Os arquivos .h do seu próprio projeto |
O gcc mostra a lista de pastas com a opção -v (de verbose, "falador"). O comando abaixo pré-processa um arquivo vazio só para ver essa lista:
$ echo | gcc -E -v - 2>&1 | sed -n "/search starts/,/End of/p" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-linux-gnu/15/include /usr/local/include /usr/include/x86_64-linux-gnu /usr/include End of search list.
Em português: "a busca do #include "..." começa aqui" (a pasta do próprio arquivo, que não aparece na lista) e "a busca do #include <...> começa aqui", seguida das pastas do sistema. O stdio.h mora em /usr/include/stdio.h.
O que acontece se a Ana escrever #include <loja.h>, com sinais de menor e maior? O pré-processador procura só nas pastas do sistema, e o loja.h não está lá:
$ gcc -std=c23 -Wall -Wextra angulo.c -o angulo
angulo.c:2:10: fatal error: loja.h: Arquivo ou diretório inexistente
2 | #include <loja.h>
| ^~~~~~~~
compilation terminated.
- fatal error
- "Erro fatal": um erro tão grave que o gcc nem continua. Sem o arquivo, não há o que compilar.
- Arquivo ou diretório inexistente
- Esta parte vem do sistema, e o sistema da Ana está em português. Em fóruns você vai ver a versão em inglês:
No such file or directory. - compilation terminated.
- "Compilação encerrada."
Dá para acrescentar uma pasta à lista do sistema com -I (i maiúsculo, de include). -I. quer dizer "procure também na pasta atual" (o ponto é a pasta atual):
$ gcc -std=c23 -Wall -Wextra -I. angulo.c -o angulo && ./angulo Lojinha da Ana: caderno a R$ 12
Funciona, mas a regra continua simples: arquivos seus, entre aspas; arquivos do sistema, entre < >. O -I é útil em projetos grandes, com os .h numa pasta include/ (você vai ver isso em Arquivos .h e .c e Bibliotecas).
No editor do guia, o Clang dá uma dica ainda mais direta para o mesmo erro:
angulo.c:2:10: error: 'loja.h' file not found with <angled> include; use "quotes" instead
2 | #include <loja.h>
| ^~~~~~~~
| "loja.h"
"loja.h não encontrado com include de sinais angulares; use aspas."
Experimente: esta caixa tem duas abas, usa_loja.c e loja.h. Rode. Depois mude o preço no loja.h e rode de novo. Por último, troque "loja.h" por <loja.h> no usa_loja.c e leia o erro.
#include <stdio.h>
#include "loja.h"
int main(void) {
printf("%s: caderno a R$ %d\n", LOJA, PRECO_CADERNO);
return 0;
}
#define LOJA "Lojinha da Ana" #define PRECO_CADERNO 12
3. #define: dar nome a um valor
A linha #define PRECO 12 ("defina PRECO como 12") cria uma macro: um nome que o pré-processador troca por um texto em todo o resto do arquivo, da linha do #define para baixo.
#define PRECO 12
- #define
- A diretiva. Como toda diretiva, começa com
#e ocupa a linha inteira. - PRECO
- O nome da macro. Por costume, macros são escritas EM MAIÚSCULAS, para que quem lê saiba na hora que aquilo não é uma variável.
- 12
- O texto de troca: tudo o que vem depois do nome, até o fim da linha.
- (sem
=e sem;) - Diretivas não são comandos de C. Não levam
=nem ponto e vírgula: se você puser, eles entram no texto de troca (veja o que acontece logo abaixo).
Por que dar nome a um número? Imagine o preço do caderno aparecendo em dez lugares do programa. Quando o preço subir, você muda uma linha, e não dez. E PRECO * 3 diz muito mais para quem lê do que 12 * 3. Um número solto no meio do código, sem nome, é chamado de número mágico, e é bom evitar.
A troca respeita palavras inteiras e textos entre aspas
O pré-processador troca só a palavra inteira PRECO. Ele não mexe num nome maior que contém PRECO, e nem em textos entre aspas:
#include <stdio.h>
#define PRECO 12
int main(void) {
printf("PRECO custa %d\n", PRECO);
int PRECO_TOTAL = 36;
printf("%d\n", PRECO_TOTAL);
return 0;
}
$ gcc -std=c23 -Wall -Wextra texto.c -o texto && ./texto PRECO custa 12 « o PRECO das aspas ficou; o de fora virou 12 36
#undef: esquecer uma macro
#undef TAXA ("undefine") apaga a macro. Daí para baixo, o nome volta a ser desconhecido:
#include <stdio.h>
#define TAXA 5
int main(void) {
printf("Taxa: %d\n", TAXA);
#undef TAXA
printf("Taxa: %d\n", TAXA);
return 0;
}
$ gcc -std=c23 -Wall -Wextra undef.c -o undef
undef.c: In function ‘main’:
undef.c:8:26: error: ‘TAXA’ undeclared (first use in this function)
8 | printf("Taxa: %d\n", TAXA);
| ^~~~
undef.c:8:26: note: each undeclared identifier is reported only once for each function it appears in
Repare que o #undef está no meio do main e funciona assim mesmo: o pré-processador não sabe o que é uma função. Ele lê o arquivo de cima para baixo, como texto. Na linha 8, TAXA não é mais macro, chega intacto ao compilador, e o compilador não conhece nenhuma variável com esse nome.
Experimente: rode. Mude o PRECO para 15 e rode de novo: só uma linha mudou, e as duas contas acompanharam. Depois acrescente #define DESCONTO 2 e subtraia o desconto do total.
#include <stdio.h>
#define LOJA "Lojinha da Ana"
#define PRECO 12
int main(void) {
printf("%s\n", LOJA);
printf("3 cadernos: R$ %d\n", PRECO * 3);
printf("5 cadernos: R$ %d\n", PRECO * 5);
return 0;
}
4. Nomes que já vêm prontos
O pré-processador já conhece alguns nomes, sem você definir nada. Eles começam e terminam com dois sublinhados (__):
#include <stdio.h>
void avisar(void) {
printf("Estou em %s, na função %s, linha %d\n", __FILE__, __func__, __LINE__);
}
int main(void) {
printf("Arquivo: %s\n", __FILE__);
printf("Linha: %d\n", __LINE__);
printf("Função: %s\n", __func__);
printf("Compilado em %s às %s\n", __DATE__, __TIME__);
printf("Versão do C: %ld\n", __STDC_VERSION__);
avisar();
return 0;
}
$ gcc -std=c23 -Wall -Wextra onde.c -o onde $ ./onde Arquivo: onde.c Linha: 9 Função: main Compilado em Oct 17 2026 às 10:32:21 Versão do C: 202311 Estou em onde.c, na função avisar, linha 4
| Nome | Vira | Tipo do valor |
|---|---|---|
__FILE__ | o nome do arquivo que está sendo compilado | texto (%s) |
__LINE__ | o número da linha em que ele aparece | inteiro (%d) |
__func__ C99 | o nome da função em que ele aparece | texto (%s) |
__DATE__ | a data da compilação, em inglês: "Oct 17 2026" | texto |
__TIME__ | a hora da compilação: "10:32:21" | texto |
__STDC_VERSION__ | a versão do C usada: 199901L (C99), 201112L (C11), 201710L (C17), 202311L (C23) | inteiro long (%ld) |
Três detalhes importantes:
__DATE__e__TIME__não são o relógio. São a data e a hora em que ogccrodou, gravadas no programa como texto. Se você rodar o./ondeamanhã, ele vai continuar dizendo "Oct 17 2026". Para a hora atual de verdade, veja Data e Hora.__LINE__é a linha onde ele está escrito. Na funçãoavisar, deu 4, e não a linha domainque chamou a função.__func__não é bem uma macro. O pré-processador nem sabe o que é uma função; quem resolve o__func__é o compilador, que cria um texto com o nome da função. Por isso ele só funciona dentro de uma função. No uso do dia a dia, ele anda junto com os outros dois.
Para que servem? Para mensagens de depuração que dizem onde o problema aconteceu. O assert (aula Testes) usa exatamente esses nomes para mostrar o arquivo, a linha e a função de quem falhou. Na aula Macros com Parâmetros você vai criar uma macro LOG que faz o mesmo.
__DATE__ e o __TIME__ mostram a hora em UTC (o horário de Greenwich, 3 horas à frente de Brasília), porque o compilador roda dentro do navegador sem saber o seu fuso. Na máquina da Ana o fuso é o de São Paulo.
5. #error e #warning: recados na hora de compilar
#error: pare tudo
Às vezes, um valor de #define não faz sentido, e é melhor descobrir isso antes de o programa existir. A Ana divide uma compra de R$ 120 em parcelas. Zero parcelas daria uma divisão por zero. Com #if (o "se" do pré-processador, assunto da aula Compilação Condicional) e #error, ela impede isso:
#include <stdio.h>
#define PARCELAS 0
#if PARCELAS == 0
#error "PARCELAS não pode ser zero: escolha 1 ou mais"
#endif
int main(void) {
printf("Cada parcela: %d\n", 120 / PARCELAS);
return 0;
}
$ gcc -std=c23 -Wall -Wextra erro.c -o erro
erro.c:6:2: error: #error "PARCELAS não pode ser zero: escolha 1 ou mais"
6 | #error "PARCELAS não pode ser zero: escolha 1 ou mais"
| ^~~~~
erro.c: In function ‘main’:
erro.c:10:38: warning: division by zero [-Wdiv-by-zero]
10 | printf("Cada parcela: %d\n", 120 / PARCELAS);
| ^
- #if PARCELAS == 0
- "Se PARCELAS for igual a 0". O pré-processador faz essa conta sozinho, antes de compilar.
- #error "…"
- "Erro": mostra a sua mensagem como um erro de compilação. O programa não é criado.
- #endif
- "Fim do if": fecha o bloco que o
#ifabriu.
Repare que o gcc mostrou o seu erro e continuou lendo o arquivo: por isso também apareceu o aviso de divisão por zero. Mesmo assim, como houve um erro, o programa erro não foi criado. Com #define PARCELAS 3, o #if é falso, a linha do #error é pulada e tudo compila.
#warning: só um lembrete C23
#warning ("aviso") mostra a mensagem como um aviso: o programa é criado normalmente. Serve para lembretes como "falta terminar isto":
#include <stdio.h>
#warning "Falta testar o desconto de aniversário"
int main(void) {
printf("Lojinha aberta!\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra aviso.c -o aviso
aviso.c:3:2: warning: #warning "Falta testar o desconto de aniversário" [-Wcpp]
3 | #warning "Falta testar o desconto de aniversário"
| ^~~~~~~
$ ./aviso
Lojinha aberta!
O #warning existia há décadas no gcc e no Clang, mas só entrou na norma oficial no C23. Com um padrão antigo e a opção -pedantic (que reclama de tudo o que não está na norma), o gcc avisa:
$ gcc -std=c17 -Wall -Wextra -pedantic aviso.c -o aviso
aviso.c:3:2: warning: ‘#warning’ before C23 is a GCC extension [-Wpedantic]
3 | #warning "Falta testar o desconto de aniversário"
| ^~~~~~~
aviso.c:3:2: warning: #warning "Falta testar o desconto de aniversário" [-Wcpp]
"#warning antes do C23 é uma extensão do GCC." Funciona do mesmo jeito; o aviso só lembra que um compilador muito estrito poderia não aceitar.
Exigir uma versão do C
Um uso clássico do #error: recusar um padrão antigo demais. Este programa usa bool sem #include, o que só funciona no C23:
#include <stdio.h>
#if __STDC_VERSION__ < 202311L
#error "Este programa precisa de C23 (use -std=c23)"
#endif
int main(void) {
bool aberta = true;
printf("Aberta? %d\n", aberta);
return 0;
}
$ gcc -std=c17 -Wall -Wextra versao.c -o versao
versao.c:4:2: error: #error "Este programa precisa de C23 (use -std=c23)"
4 | #error "Este programa precisa de C23 (use -std=c23)"
| ^~~~~
versao.c: In function ‘main’:
versao.c:8:5: error: unknown type name ‘bool’
8 | bool aberta = true;
| ^~~~
« … mais algumas linhas sobre bool e true …
$ gcc -std=c23 -Wall -Wextra versao.c -o versao && ./versao
Aberta? 1
A primeira mensagem já explica o que fazer, em português, escrita por quem fez o programa. Os erros seguintes (sobre bool) são consequência.
Missão: o programa compila e mostra as parcelas. Mude PARCELAS para 0 e rode: o #error deve impedir a compilação.
#include <stdio.h>
#define TOTAL 120
#define PARCELAS 3
#if PARCELAS == 0
#error "PARCELAS não pode ser zero: escolha 1 ou mais"
#endif
int main(void) {
printf("%d parcelas de R$ %d\n", PARCELAS, TOTAL / PARCELAS);
return 0;
}
#include <stdio.h>
#define TOTAL 120
#define PARCELAS 0
#if PARCELAS == 0
#error "PARCELAS não pode ser zero: escolha 1 ou mais"
#endif
int main(void) {
printf("%d parcelas de R$ %d\n", PARCELAS, TOTAL / PARCELAS);
return 0;
}
Formas diferentes: #define, const, constexpr ou enum?
O #define é o jeito mais antigo de dar nome a um valor, e você vai vê-lo em quase todo código C. Mas não é o único. Os quatro abaixo compilam juntos no gcc 15:
#include <stdio.h>
#define PRECO 12
constexpr int QTD = 3;
enum { DESCONTO = 2 };
int main(void) {
printf("Total: %d\n", PRECO * QTD - DESCONTO);
return 0;
}
$ gcc -std=c23 -Wall -Wextra juntas.c -o juntas && ./juntas Total: 34
| Forma | Quem cuida | Tem tipo? | Quando usar |
|---|---|---|---|
#define PRECO 12 | pré-processador (troca de texto) | não | Código que precisa funcionar em qualquer versão do C; valores usados em #if; textos fixos |
const int PRECO = 12; | compilador | sim | Valores que não devem mudar dentro de uma função |
constexpr int PRECO = 12; C23 | compilador | sim | Constantes de verdade, com tipo, conhecidas na compilação (servem até para o tamanho de um array) |
enum { PRECO = 12 }; | compilador | sim (int) | Grupos de números inteiros com nome (veja Enums) |
Qual usar? Em código novo com C23, prefira constexpr para números: o compilador conhece o tipo, mostra o nome nas mensagens de erro e o depurador enxerga a constante. Use #define quando precisar do pré-processador de verdade: valores testados com #if, macros com parâmetros e compilação condicional. A aula Constantes compara as formas em detalhe.
Quando dá errado
fatal error: lojas.h: Arquivo ou diretório inexistente
$ gcc -std=c23 -Wall -Wextra errado.c -o errado
errado.c:2:10: fatal error: lojas.h: Arquivo ou diretório inexistente
2 | #include "lojas.h"
| ^~~~~~~~~
compilation terminated.
O que significa: o pré-processador não achou o arquivo do #include (em inglês, No such file or directory; no editor, 'lojas.h' file not found). Ou o nome está errado (aqui, lojas.h em vez de loja.h), ou o arquivo está em outra pasta, ou você usou < > para um arquivo seu.
Como consertar: confira o nome letra por letra (maiúsculas contam no Linux) e veja com ls se o arquivo está na mesma pasta do .c. Arquivo seu vai entre aspas. Se ele está em outra pasta, use -I pasta.
error: invalid type argument of unary ‘*’ (have ‘int’)
#include <stdio.h>
#define PRECO 12;
int main(void) {
int total = PRECO * 3;
printf("Total: %d\n", total);
return 0;
}
$ gcc -std=c23 -Wall -Wextra pv.c -o pv
pv.c: In function ‘main’:
pv.c:5:23: error: invalid type argument of unary ‘*’ (have ‘int’)
5 | int total = PRECO * 3;
| ^~~
O que significa: o ; do #define entrou no texto de troca. A linha 5 virou int total = 12; * 3;. A primeira parte é uma declaração completa; a segunda, * 3;, não faz sentido. O erro aponta uma linha que parece certa, porque o defeito está lá em cima, na linha do #define.
Como consertar: tire o ; do #define. Quando um erro esquisito aparece numa linha que usa uma macro, rode gcc -E -P e veja o texto de verdade.
error: expected expression before ‘=’ token + note: in expansion of macro ‘PRECO’
$ gcc -std=c23 -Wall -Wextra igual.c -o igual
igual.c: In function ‘main’:
igual.c:2:15: error: expected expression before ‘=’ token
2 | #define PRECO = 12
| ^
igual.c:5:17: note: in expansion of macro ‘PRECO’
5 | int total = PRECO * 3;
| ^~~~~
O que significa: foi escrito #define PRECO = 12, como numa variável. O = virou parte do texto e a linha 5 ficou int total = = 12 * 3;. A note ("nota") ajuda: "na expansão da macro PRECO".
Como consertar: #define NOME valor, só com espaço entre o nome e o valor.
error: invalid preprocessing directive #inclue; did you mean #include?
$ gcc -std=c23 -Wall -Wextra inclue.c -o inclue
inclue.c:1:2: error: invalid preprocessing directive #inclue; did you mean #include?
1 | #inclue <stdio.h>
| ^~~~~~
| include
inclue.c: In function ‘main’:
inclue.c:4:5: error: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
« … mais linhas sobre o printf …
O que significa: "diretiva de pré-processamento inválida #inclue; você quis dizer #include?". Uma letra faltando. Como o stdio.h não foi incluído, vêm depois os erros do printf.
Como consertar: corrija a diretiva. Regra geral: conserte sempre o primeiro erro e compile de novo; muitas vezes os outros somem junto.
warning: ‘PRECO’ redefined
$ gcc -std=c23 -Wall -Wextra redef.c -o redef
redef.c:3:9: warning: ‘PRECO’ redefined
3 | #define PRECO 15
| ^~~~~
redef.c:2:9: note: this is the location of the previous definition
2 | #define PRECO 12
| ^~~~~
O que significa: o mesmo nome foi definido duas vezes com valores diferentes ("PRECO redefinido"; a nota diz "esta é a posição da definição anterior"). Vale a última, mas é quase sempre um engano, muitas vezes porque dois arquivos .h definem o mesmo nome.
Como consertar: deixe uma definição só. Se a troca é de propósito, faça #undef PRECO antes do novo #define.
Referência
| Escrita | O que faz |
|---|---|
#include <arquivo.h> | Cola um arquivo do sistema (procura em /usr/include e companhia) |
#include "arquivo.h" | Cola um arquivo seu (procura primeiro na pasta do .c) |
#define NOME valor | Cria uma macro: NOME vira valor daí para baixo |
#undef NOME | Apaga a macro |
#error "texto" | Para a compilação com a mensagem |
#warning "texto" C23 | Mostra um aviso e continua |
__FILE__, __LINE__, __func__ | Arquivo, linha e função atuais |
__DATE__, __TIME__ | Data e hora da compilação (texto) |
__STDC_VERSION__ | Versão do C: 202311L no C23 |
gcc -E arquivo.c | Mostra o resultado do pré-processamento, com marcas de linha |
gcc -E -P arquivo.c | O mesmo, sem as marcas |
gcc -I pasta … | Acrescenta uma pasta à busca do #include |
Exercícios
1. Ver o que o compilador recebe
Complete o comando que mostra o arquivo depois do pré-processamento, sem as marcas de linha:
2. Incluir um arquivo seu
Complete a linha que inclui o arquivo cardapio.h, que está na mesma pasta do seu .c:
3. Uma constante
Complete para que MAX_ALUNOS vire 40 em todo o arquivo:
4. Com #define PRECO 12; (com ponto e vírgula), em que se transforma int x = PRECO * 2;?
5. Um programa com printf("%s\n", __DATE__); foi compilado em 17 de outubro e rodado em 20 de outubro. O que ele mostra?
6. Qual é a diferença entre #error e #warning?
Missão final: a etiqueta da lojinha
Missão: o programa não compila: faltam as macros PRECO e QTD. Crie as duas com #define no lugar indicado (PRECO vale 15, QTD vale 4, sem = e sem ;) e rode. A última linha mostra, com __LINE__, a linha em que o printf dela está escrito. Sem avisos.
Lojinha da Ana 4 cadernos x R$ 15 = R$ 60 (etiqueta impressa pela linha 13 do programa)
#include <stdio.h>
#define LOJA "Lojinha da Ana"
// crie aqui PRECO (15)
// crie aqui QTD (4)
int main(void) {
int total = PRECO * QTD;
printf("%s\n", LOJA);
printf("%d cadernos x R$ %d = R$ %d\n", QTD, PRECO, total);
printf("(etiqueta impressa pela linha %d do programa)\n", __LINE__);
return 0;
}
#include <stdio.h>
#define LOJA "Lojinha da Ana"
#define PRECO 15
#define QTD 4
int main(void) {
int total = PRECO * QTD;
printf("%s\n", LOJA);
printf("%d cadernos x R$ %d = R$ %d\n", QTD, PRECO, total);
printf("(etiqueta impressa pela linha %d do programa)\n", __LINE__);
return 0;
}
Se a última linha mostrar outro número, é porque você acrescentou ou apagou linhas acima do printf: o __LINE__ sempre conta a linha em que está escrito. Volte o código a ter as macros nas linhas 4 e 5, no lugar dos comentários.
Resumo
- O pré-processador roda antes do compilador. Ele segue as diretivas (linhas com
#), apaga os comentários e entrega um texto C puro. Ele não entende C: só copia, troca e apaga texto. gcc -E arquivo.cmostra esse texto (-Ptira as marcas de linha). É o raio X para entender macros.#includecola um arquivo inteiro.<stdio.h>procura nas pastas do sistema;"loja.h"procura primeiro na pasta do.c. Arquivo seu, entre aspas.#define NOME valorcria uma macro, sem=e sem;. A troca vale para palavras inteiras, fora de aspas, daí para baixo.#undefapaga.__FILE__,__LINE__e__func__dizem onde você está;__DATE__e__TIME__guardam o momento da compilação;__STDC_VERSION__diz a versão do C.#errorimpede a compilação com uma mensagem sua;#warning(oficial no C23) só avisa.- Para números em código novo,
constexpr(C23) costuma ser melhor que#define. A seguir: Macros com Parâmetros.