C++ O Pré-processador
Desde o primeiro programa você escreve #include <iostream> no topo do arquivo. Essa linha começa com # e não termina com ;, e isso tem um motivo: ela não é C++. É uma ordem para um programa que trabalha antes do compilador, o pré-processador. Nesta aula você vai ver o que ele faz com o seu arquivo, aprender as ordens que ele entende (#include, #define, #ifdef…) e conhecer as armadilhas que fizeram os programadores de C++ moderno usarem o pré-processador o mínimo possível.
- O que é o pré-processador e em que momento ele roda.
- A diferença entre
#include <iostream>e#include "meu.h". - Como criar constantes e macros com
#define, e por queDOBRO(1+2)pode dar 5. - Como ligar e desligar pedaços do código com
#ifdef,#ifndefe#if. - Como definir um nome na hora de compilar, com
g++ -D. - Como ver o arquivo que o compilador recebe de verdade, com
g++ -E. - Para que servem
__FILE__,__LINE__e#pragma once.
Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cpp/prepro, com o comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa (veja Compilar com g++). 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 receitas escritas por inteiro. Antes de a receita chegar até ela, um assistente passa pelo papel e faz trocas mecânicas:
- onde está escrito "cole aqui a receita do molho", ele cola o texto inteiro da receita do molho;
- onde está escrito "XÍCARA", ele troca por "240 ml", porque alguém avisou lá em cima que XÍCARA quer dizer 240 ml;
- os trechos marcados "só se for para festa" ele recorta fora, se não for festa.
O assistente não sabe cozinhar. Ele não entende o que está escrito; ele só cola, troca e recorta 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").
| Etapa | Quem faz | O que recebe | O que entrega |
|---|---|---|---|
| 1. Pré-processamento | o pré-processador | o seu .cpp, com as linhas # | um texto C++ sem nenhuma linha # (as diretivas já foram cumpridas) |
| 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 g++ arquivo.cpp -o programa, 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.
1. A regra de ouro: só troca de texto
Comece pelo exemplo mais famoso. A Ana quer uma "função" que dobra um número e escreve, usando o pré-processador:
#include <iostream>
#define DOBRO(x) x*2
int main() {
std::cout << "DOBRO(4) = " << DOBRO(4) << '\n';
std::cout << "DOBRO(1+2) = " << DOBRO(1+2) << '\n';
}
- #define
- "Defina": cria um nome que o pré-processador vai trocar por outro texto. Um nome assim se chama macro.
- DOBRO(x)
- O nome da macro, com um parâmetro
x. Por costume, macros são escritas EM MAIÚSCULAS, para chamar a atenção. - x*2
- O texto que vai entrar no lugar de
DOBRO(…), com oxtrocado pelo que estiver entre os parênteses. - (sem ;)
- Diretivas não terminam com ponto e vírgula. A linha acaba e a diretiva acaba junto.
$ g++ -std=c++23 -Wall -Wextra dobro.cpp -o dobro $ ./dobro DOBRO(4) = 8 DOBRO(1+2) = 5 « 1+2 é 3, e o dobro de 3 é 6, não 5!
Nenhum erro, nenhum aviso, e uma resposta errada. Para entender, peça ao g++ para fazer só o pré-processamento e mostrar o resultado. A opção é -E. Num arquivo pequeno, sem #include, dá para ler tudo:
#define PRECO 10
#define DOBRO(x) x*2
int main() {
int a = PRECO;
int b = DOBRO(1+2);
return a + b;
}
$ g++ -E mini.cpp
# 0 "mini.cpp"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "mini.cpp"
int main() {
int a = 10;
int b = 1+2*2; « o texto "1+2" entrou no lugar do x
return a + b;
}
Aí está o problema. O pré-processador não calculou nada: trocou x pelo texto 1+2 e entregou 1+2*2 ao compilador. Como a multiplicação vem antes da soma (veja Precedência), o resultado é 1 + 4 = 5.
- -E
- "Pare depois do pré-processamento" (E de expand, expandir). O resultado sai na tela, ou num arquivo com
-o. Nada é compilado. - linhas
# 1 "mini.cpp" - Marcas que o pré-processador deixa para o compilador saber de que arquivo e de que linha veio cada pedaço. É assim que as mensagens de erro conseguem dizer
mini.cpp:6. - linhas vazias
- Onde estavam os
#define. A diretiva foi cumprida e sumiu.
Para ver só o código, sem as marcas, acrescente -P:
$ g++ -E -P mini.cpp
int main() {
int a = 10;
int b = 1+2*2;
return a + b;
}
O conserto: parênteses em tudo
Quem escreve macros aprende a cercar cada parâmetro e a macro inteira de parênteses:
#define DOBRO(x) ((x) * 2)
Agora DOBRO(1+2) vira ((1+2) * 2), que dá 6. Os parênteses de fora protegem contra outro tropeço: com #define DOBRO(x) (x)*2, a conta 100 / DOBRO(5) vira 100 / (5)*2, que o C++ faz da esquerda para a direita: 20 * 2 = 40, e não 100 / 10 = 10.
Uma função de verdade recebe o valor já calculado, não o texto, e não tem esse problema:
constexpr int dobro(int x) { return x * 2; } // dobro(1+2) dá 6
Ela é tão rápida quanto a macro, o compilador confere os tipos e ela aparece com o nome certo no depurador. Você vai ver funções em Funções e o constexpr em constexpr e consteval.
Experimente: rode e veja o 5 errado. Depois troque a definição por #define DOBRO(x) ((x) * 2) e rode de novo. Para comparar, a função dobro já está pronta embaixo.
#include <iostream>
#define DOBRO(x) x*2
constexpr int dobro(int x) { return x * 2; }
int main() {
std::cout << "macro: DOBRO(1+2) = " << DOBRO(1+2) << '\n';
std::cout << "função: dobro(1+2) = " << dobro(1+2) << '\n';
}
2. #include: colar outro arquivo aqui
#include quer dizer "inclua". O pré-processador abre o arquivo indicado e cola o conteúdo inteiro no lugar da linha. Veja o tamanho da colagem num "Olá" de cinco linhas:
#include <iostream>
int main() {
std::cout << "Olá!\n";
}
$ g++ -std=c++23 -E ola.cpp -o ola.ii
$ wc -l ola.ii
68846 ola.ii « 68 mil linhas!
$ tail -5 ola.ii
# 3 "ola.cpp"
int main() {
std::cout << "Olá!\n";
}
O seu programa de 5 linhas virou um arquivo de quase 69 mil linhas: o <iostream> inclui outros arquivos, que incluem outros, e tudo é colado. Só no final aparece o seu main. (O .ii é a extensão costumeira para "C++ já pré-processado"; wc -l conta linhas.) É por isso que compilar C++ pode demorar, e é um dos motivos dos Módulos do C++20.
Com < > ou com " "?
As duas formas fazem a mesma colagem. A diferença é onde o pré-processador procura o arquivo:
| Forma | Onde procura | Use para |
|---|---|---|
#include <iostream> | só nas pastas do sistema (as da biblioteca padrão e das bibliotecas instaladas) | biblioteca padrão e bibliotecas de terceiros |
#include "calculos.h" | primeiro na pasta do arquivo que está incluindo; se não achar, nas pastas do sistema | os arquivos do seu próprio projeto |
Dá para ver a lista de pastas pedindo ao g++ que conte o que está fazendo (-v, de verbose, "falante"). O comando é comprido porque pré-processa um arquivo vazio só para mostrar a lista:
$ echo | g++ -std=c++23 -x c++ -E -v - 2>&1 | sed -n '/search starts here/,/End of search/p' #include "..." search starts here: #include <...> search starts here: /usr/include/c++/15 « aqui mora o arquivo iostream /usr/include/x86_64-linux-gnu/c++/15 /usr/include/c++/15/backward /usr/lib/gcc/x86_64-linux-gnu/15/include /usr/local/include /usr/include End of search list.
A lista das aspas está vazia porque, para elas, a primeira pasta é sempre a do próprio arquivo, e só depois vem a lista de baixo. O <iostream> é o arquivo /usr/include/c++/15/iostream (sem extensão mesmo).
A Ana criou um arquivo precos.h na mesma pasta e o incluiu com sinais de menor e maior:
#include <iostream>
#include <precos.h>
int main() {
std::cout << "Pão: R$ " << PRECO_PAO << '\n';
}
$ g++ -std=c++23 -Wall -Wextra loja.cpp -o loja
loja.cpp:2:10: fatal error: precos.h: Arquivo ou diretório inexistente
2 | #include <precos.h>
| ^~~~~~~~~~
compilation terminated.
O que significa: fatal error é um erro que faz o g++ desistir na hora (compilation terminated, "compilação encerrada"). Com < >, ele procurou precos.h só nas pastas do sistema e não achou. A parte "Arquivo ou diretório inexistente" vem em português porque é o sistema que diz que o arquivo não existe; o resto da mensagem é do g++, em inglês.
Como resolver: para arquivos seus, use aspas: #include "precos.h". Outra saída é acrescentar uma pasta à lista do sistema com -I (de include): -I. quer dizer "procure também na pasta atual".
$ g++ -std=c++23 -Wall -Wextra -I. loja.cpp -o loja $ ./loja Pão: R$ 0.75
O mesmo erro aparece se o nome estiver errado ("preco.h" no lugar de "precos.h") ou se o arquivo estiver em outra pasta. Confira com ls.
Como separar um programa em arquivos .h e .cpp é o assunto da próxima aula, Arquivos .h e .cpp.
3. #define para constantes
A forma mais simples do #define não tem parâmetros: só troca um nome por um valor. Você vai encontrá-la em muito código antigo e em código C:
#define PRECO 10 // onde aparecer PRECO, escreva 10 #define PI 3.14159 #define LOJA "Lojinha da Ana"
Em C++, existe um jeito melhor de criar constantes, que você já viu em Constantes (const e constexpr):
constexpr int PRECO = 10; constexpr double PI = 3.14159;
#define PRECO 10 | constexpr int PRECO = 10; | |
|---|---|---|
| Tem tipo? | não, é só texto | sim, int |
| Respeita escopo (chaves, namespaces)? | não: vale do #define até o fim do arquivo, em todo lugar | sim |
| Aparece nas mensagens de erro e no depurador? | não, o compilador só vê 10 | sim, pelo nome |
Termina com ;? | não (veja o cenário abaixo) | sim |
Qual usar: constexpr (ou const) para constantes. Deixe o #define para o que só o pré-processador consegue fazer, como as compilações condicionais da próxima seção.
; a mais no #define
#include <iostream>
#define PRECO 10;
int main() {
int total = PRECO * 3;
std::cout << total << '\n';
}
$ g++ -std=c++23 -Wall -Wextra ponto.cpp -o ponto
ponto.cpp: In function ‘int main()’:
ponto.cpp:6:23: error: invalid type argument of unary ‘*’ (have ‘int’)
6 | int total = PRECO * 3;
| ^~~
O que aconteceu: o ; faz parte do texto da macro. A linha 6 virou int total = 10; * 3;. O compilador lê int total = 10; e depois * 3; sozinho, e reclama que o * "de um operando só" (unary) não serve para um int. A mensagem fala de * e não de PRECO, porque o compilador nunca viu a palavra PRECO: esse é o pior defeito das macros, o erro aparece longe da causa.
Como resolver: tire o ; do #define. Melhor ainda: troque por constexpr int PRECO = 10;.
#include <iostream>
#define DESCONTO 10
#define DESCONTO 15
int main() {
std::cout << DESCONTO << '\n';
}
$ g++ -std=c++23 -Wall -Wextra redef.cpp -o redef
redef.cpp:4:9: warning: ‘DESCONTO’ redefined
4 | #define DESCONTO 15
| ^~~~~~~~
redef.cpp:3:9: note: this is the location of the previous definition
3 | #define DESCONTO 10
| ^~~~~~~~
$ ./redef
15
O que significa: "‘DESCONTO’ foi redefinido". A segunda definição venceu, e a nota (note) mostra onde estava a primeira. Em programas grandes, isso costuma acontecer quando dois arquivos incluídos usam o mesmo nome de macro, e é um ótimo motivo para preferir constexpr, que daria um erro claro.
Como resolver: apague uma das definições. Se você quer mesmo trocar o valor, desfaça a primeira antes com #undef DESCONTO ("desdefina").
4. Ligar e desligar código: #ifdef, #ifndef e #if
Aqui o pré-processador faz algo que o C++ comum não faz: recortar pedaços do código antes de compilar. O uso mais comum é deixar mensagens de depuração no programa e ligá-las só quando precisar:
#include <iostream>
int main() {
int total = 42;
#ifdef DEBUG
std::cout << "[debug] total vale " << total << '\n';
#endif
std::cout << "Total: " << total << '\n';
}
- #ifdef DEBUG
- "Se
DEBUGestiver definido" (if defined). Só pergunta se o nome existe; o valor não importa. - a linha do meio
- Fica no código só se a resposta for sim. Se for não, o pré-processador a apaga e o compilador nem fica sabendo que ela existiu.
- #endif
- "Fim do se". Todo
#ifdef,#ifndefe#ifprecisa do seu.
E onde se define DEBUG? Pode ser com #define DEBUG no topo do arquivo, mas o jeito prático é na linha de comando, com -D (de define), sem mexer no código:
$ g++ -std=c++23 -Wall -Wextra debug.cpp -o debug $ ./debug Total: 42 $ g++ -std=c++23 -Wall -Wextra -DDEBUG debug.cpp -o debug $ ./debug [debug] total vale 42 Total: 42
-DDEBUG (tudo junto) é o mesmo que escrever #define DEBUG na primeira linha do arquivo. Para dar um valor, use =: -DTAXA=8 equivale a #define TAXA 8.
#ifndef: um valor padrão que a linha de comando pode trocar
#ifndef é o contrário: "se não estiver definido" (if not defined). Junto com #if, que testa uma conta, e #elif/#else, que funcionam como o else if e o else:
#include <iostream>
#ifndef TAXA
#define TAXA 5
#endif
#define NIVEL 2
int main() {
std::cout << "Taxa: " << TAXA << "%\n";
#if NIVEL >= 2
std::cout << "Mostrando detalhes (nível " << NIVEL << ")\n";
#elif NIVEL == 1
std::cout << "Só o resumo\n";
#else
std::cout << "Silêncio\n";
#endif
}
$ g++ -std=c++23 -Wall -Wextra taxa.cpp -o taxa $ ./taxa Taxa: 5% Mostrando detalhes (nível 2) $ g++ -std=c++23 -Wall -Wextra -DTAXA=8 taxa.cpp -o taxa $ ./taxa Taxa: 8% « o -D chegou primeiro, e o #ifndef não trocou Mostrando detalhes (nível 2)
| Diretiva | Pergunta | Exemplo |
|---|---|---|
#ifdef X | X está definido? | #ifdef DEBUG |
#ifndef X | X não está definido? | #ifndef TAXA |
#if conta | a conta (só com números inteiros e macros) dá diferente de zero? | #if NIVEL >= 2 |
#if defined(X) | o mesmo que #ifdef, mas pode ser combinado | #if defined(DEBUG) && NIVEL > 1 |
#elif / #else | senão, se… / senão | |
#endif | fim do bloco |
#if no lugar do if
O #if decide na hora de compilar, olhando só macros e números escritos no código. Ele não enxerga variáveis: #if idade >= 18 não funciona, porque a variável idade só ganha valor quando o programa roda. Para decisões do programa, use o if normal.
Experimente: rode como está. Depois apague (ou comente com //) a linha #define DEBUG e rode de novo: as linhas [debug] somem. Por último, troque #define NIVEL 2 por 1 e por 0.
#include <iostream>
#define DEBUG
#define NIVEL 2
int main() {
int itens = 3;
double total = 27.5;
#ifdef DEBUG
std::cout << "[debug] itens = " << itens << '\n';
std::cout << "[debug] total = " << total << '\n';
#endif
#if NIVEL >= 2
std::cout << "Compra com " << itens << " itens: R$ " << total << '\n';
#elif NIVEL == 1
std::cout << "R$ " << total << '\n';
#else
std::cout << "(modo silencioso)\n";
#endif
}
#error: parar a compilação com uma explicação
O compilador também define macros sozinho. Uma delas, __cplusplus, diz qual versão do C++ está valendo: 202302 com -std=c++23 e 201703 (C++17) se você não pedir nenhuma, no g++ 15. Junto com #error, dá para avisar quem esqueceu a opção:
#include <iostream>
#if __cplusplus < 202302L
#error "Este programa precisa de C++23: compile com -std=c++23"
#endif
int main() {
std::cout << "Ok, C++23!\n";
}
$ g++ -Wall -Wextra versao.cpp -o versao
versao.cpp:4:2: error: #error "Este programa precisa de C++23: compile com -std=c++23"
4 | #error "Este programa precisa de C++23: compile com -std=c++23"
| ^~~~~
$ g++ -std=c++23 -Wall -Wextra versao.cpp -o versao && ./versao
Ok, C++23!
O L no fim de 202302L marca o número como long; é assim que o padrão escreve esses valores (veja Versões do C++).
5. Onde estou? __FILE__ e __LINE__
Algumas macros já vêm prontas e mudam de valor conforme o lugar onde aparecem. São úteis em mensagens de erro e de depuração:
#include <iostream>
int main() {
std::cout << "Arquivo: " << __FILE__ << '\n';
std::cout << "Linha: " << __LINE__ << '\n';
std::cout << "Função: " << __func__ << '\n';
std::cout << "Compilado em: " << __DATE__ << '\n';
std::cout << "__cplusplus = " << __cplusplus << '\n';
}
$ g++ -std=c++23 -Wall -Wextra onde.cpp -o onde $ ./onde Arquivo: onde.cpp Linha: 5 Função: main Compilado em: Oct 16 2026 __cplusplus = 202302
| Nome | Vira | Observação |
|---|---|---|
__FILE__ | o nome do arquivo, como texto | do jeito que foi passado ao g++ |
__LINE__ | o número da linha onde está escrito | um número inteiro |
__func__ | o nome da função atual | não é macro (é uma variável que o compilador cria), mas é usado junto |
__DATE__ / __TIME__ | data e hora da compilação | data em inglês: Oct = outubro |
__cplusplus | a versão do C++ | 201703, 202002, 202302… |
Os dois sublinhados no começo e no fim são uma convenção: nomes assim são reservados para o compilador e a biblioteca. Não crie nomes seus com __.
O cabeçalho <source_location> traz std::source_location::current(), que guarda arquivo, linha e função sem macros: loc.file_name(), loc.line(), loc.function_name(). Você vai vê-lo em código novo; as macros continuam por toda parte em código antigo e em bibliotecas.
6. #pragma once: "inclua só uma vez"
#pragma é uma diretiva de "pedidos especiais" ao compilador. A mais usada de longe é #pragma once ("uma vez só"), escrita na primeira linha de um arquivo .h: se o mesmo arquivo for incluído duas vezes, o pré-processador cola o conteúdo só na primeira.
#pragma once constexpr double PRECO_PAO = 0.75;
Por que um arquivo seria incluído duas vezes, o que dá errado quando isso acontece e qual é a alternativa clássica (os include guards, feitos com #ifndef/#define/#endif) são assuntos da aula Arquivos .h e .cpp.
Mais armadilhas das macros
Mesmo com todos os parênteses, uma macro pode avaliar o parâmetro duas vezes, porque o texto é copiado duas vezes:
#include <iostream>
#define QUADRADO(x) ((x) * (x))
int main() {
int i = 3;
int q = QUADRADO(i++);
std::cout << "q = " << q << ", i = " << i << '\n';
}
$ g++ -std=c++23 -Wall -Wextra quadrado.cpp -o quadrado
quadrado.cpp: In function ‘int main()’:
quadrado.cpp:7:23: warning: operation on ‘i’ may be undefined [-Wsequence-point]
7 | int q = QUADRADO(i++);
| ^
quadrado.cpp:3:23: note: in definition of macro ‘QUADRADO’
3 | #define QUADRADO(x) ((x) * (x))
| ^
$ ./quadrado
q = 12, i = 5
A linha virou ((i++) * (i++)): o i foi aumentado duas vezes e o resultado (12) não é nem 9 nem 16. O aviso diz "a operação sobre ‘i’ pode ser indefinida": mexer duas vezes na mesma variável numa só conta é comportamento indefinido, e outro compilador pode dar outro número. A nota mostra que o problema nasceu dentro da macro. Com uma função (constexpr int quadrado(int x)), o i++ rodaria uma vez só.
| Para… | Evite | Prefira |
|---|---|---|
| uma constante | #define PRECO 10 | constexpr int PRECO = 10; |
| uma "função" pequena | #define DOBRO(x) ((x) * 2) | constexpr int dobro(int x) { return x * 2; } |
| colar um cabeçalho | #include (não há outro jeito, fora os módulos) | |
| código só de depuração, ou só para um sistema | #ifdef / #if + -D | |
proteger um .h | #pragma once ou include guards |
Referência
| Diretiva ou opção | O que faz |
|---|---|
#include <arq> | cola o arquivo, procurando nas pastas do sistema |
#include "arq" | cola o arquivo, procurando primeiro na pasta do arquivo atual |
#define NOME texto | troca NOME por texto daqui até o fim do arquivo |
#define F(x) ((x) * 2) | macro com parâmetro |
#undef NOME | esquece a macro |
#ifdef / #ifndef / #if / #elif / #else / #endif | mantém ou recorta trechos do código |
#error "texto" | para a compilação com a mensagem |
#pragma once | inclui este arquivo só uma vez |
g++ -E arq.cpp | só pré-processa e mostra o resultado (-P tira as marcas #) |
g++ -DNOME / -DNOME=valor | define uma macro na linha de comando |
g++ -I pasta | acrescenta uma pasta à busca do #include |
__FILE__, __LINE__, __DATE__, __cplusplus | macros prontas |
Exercícios
1. Ver o que o compilador recebe
Complete o comando que mostra o arquivo mini.cpp já pré-processado, sem compilar:
2. Ligar a depuração sem mexer no código
Complete para compilar debug.cpp com a macro DEBUG definida:
3. Com #define TRIPLO(x) x*3, quanto vale TRIPLO(2+1)?
4. A Ana escreveu #include <estoque.h> para um arquivo que está na mesma pasta do main.cpp e recebeu fatal error: estoque.h: Arquivo ou diretório inexistente. Qual é o conserto mais simples?
5. Qual destas tarefas só o pré-processador consegue fazer?
Missão final: conserte as macros da lojinha
Missão: o programa abaixo mostra três números errados por causa das macros. Conserte só as linhas #define (faltam parênteses) para que a saída fique assim:
DOBRO(1+2) = 6 100 / DOBRO(5) = 10 Preço com taxa: 110
#include <iostream>
#define DOBRO(x) x*2
#define COM_TAXA(p) p + p / 10
int main() {
std::cout << "DOBRO(1+2) = " << DOBRO(1+2) << '\n';
std::cout << "100 / DOBRO(5) = " << 100 / DOBRO(5) << '\n';
std::cout << "Preço com taxa: " << COM_TAXA(50) * 2 << '\n';
}
#include <iostream>
#define DOBRO(x) ((x) * 2)
#define COM_TAXA(p) ((p) + (p) / 10)
int main() {
std::cout << "DOBRO(1+2) = " << DOBRO(1+2) << '\n';
std::cout << "100 / DOBRO(5) = " << 100 / DOBRO(5) << '\n';
std::cout << "Preço com taxa: " << COM_TAXA(50) * 2 << '\n';
}
Missão bônus: sem mexer em nada dentro do main, acrescente linhas #define no topo para que o programa mostre Modo: teste e Taxa: 8. Dica: o #ifndef TAXA só põe 5 se ninguém tiver definido TAXA antes dele.
#include <iostream>
// escreva os seus #define aqui
#ifndef TAXA
#define TAXA 5
#endif
int main() {
#ifdef MODO_TESTE
std::cout << "Modo: teste\n";
#else
std::cout << "Modo: normal\n";
#endif
std::cout << "Taxa: " << TAXA << '\n';
}
#include <iostream>
#define MODO_TESTE
#define TAXA 8
#ifndef TAXA
#define TAXA 5
#endif
int main() {
#ifdef MODO_TESTE
std::cout << "Modo: teste\n";
#else
std::cout << "Modo: normal\n";
#endif
std::cout << "Taxa: " << TAXA << '\n';
}
Resumo
- O pré-processador roda antes do compilador e só mexe em texto: cola arquivos, troca nomes, recorta trechos. As ordens para ele começam com
#e não têm;. #include <x>procura nas pastas do sistema;#include "x"procura primeiro na pasta do arquivo. Use aspas para os seus arquivos.#definetroca texto, sem entender C++:DOBRO(1+2)comx*2vira1+2*2 = 5. Em macros, parênteses em cada parâmetro e em volta de tudo.- Para constantes e "funções", prefira
constexpr: tem tipo, respeita escopo e dá erros claros. #ifdef,#ifndef,#if…#endifligam e desligam código na hora de compilar;g++ -DNOMEdefine o nome sem mexer no arquivo.g++ -E(com-P) mostra o que o compilador recebe de verdade: é o raio-x de qualquer problema com macros.__FILE__e__LINE__dizem onde você está;#pragma onceprotege um.hde ser colado duas vezes.