C Macros com Parâmetros
Na aula Pré-processador, o #define trocava um nome por um valor fixo: PRECO virava 12. Uma macro também pode receber parâmetros, como uma função: QUADRADO(4) vira ((4) * (4)). Parece uma função, mas não é. É troca de texto, e essa diferença produz alguns dos bugs mais famosos do C. Nesta aula você vai escrever macros com parâmetros, ver cada armadilha acontecer de verdade e aprender as regras que os programadores de C usam para se proteger delas.
- Como criar uma macro com parâmetros:
#define QUADRADO(x) ((x) * (x)). - Por que
QUADRADO(2+3)pode dar 11, e a regra dos parênteses. - Por que
QUADRADO(i++)soma 2 noi(o efeito duplo). - Os operadores
#(transforma em texto) e##(cola dois pedaços). - Como escrever macros de várias linhas com
\e por que elas usamdo { … } while (0). - Macros com número variável de argumentos (
...e__VA_ARGS__). - Quando usar uma função
inlineem vez de uma macro.
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/macros, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa. Para entender cada macro, vamos usar muito o gcc -E -P, que mostra o código depois do pré-processamento (veja Pré-processador).
A ideia: um carimbo com lacuna
Pense num carimbo de papelaria escrito "Pago: R$ ____". Toda vez que você carimba, escreve um valor diferente na lacuna. O carimbo não confere nada: se você escrever "banana" na lacuna, o papel fica "Pago: R$ banana".
Uma macro com parâmetro é esse carimbo. O #define diz o texto do carimbo, e o parâmetro é a lacuna. Onde você escreve QUADRADO(4), o pré-processador carimba o texto da macro e escreve 4 em cada lacuna. Ele não calcula, não confere o tipo, não sabe o que é um número. Uma função de verdade (aula Funções) é diferente: ela recebe o valor já calculado, confere o tipo e roda quando o programa roda.
| Macro com parâmetro | Função | |
|---|---|---|
| Quem cuida | o pré-processador, antes de compilar | o compilador, e ela roda com o programa |
| O que recebe | o texto que você escreveu | o valor já calculado |
| Confere o tipo? | não | sim |
| O argumento é calculado | quantas vezes ele aparecer na macro | uma vez só |
1. A primeira macro com parâmetro
A Ana quer calcular a área de mesas quadradas para a lojinha. Ela escreve uma macro que eleva um número ao quadrado:
#include <stdio.h>
#define QUADRADO(x) x * x
int main(void) {
printf("QUADRADO(4) = %d\n", QUADRADO(4));
printf("QUADRADO(2+3) = %d\n", QUADRADO(2+3));
return 0;
}
- QUADRADO
- O nome da macro, em maiúsculas por costume.
- (x)
- A lista de parâmetros, colada no nome, sem espaço. O
xé a lacuna do carimbo. - x * x
- O texto de troca. Cada
xserá trocado pelo texto que estiver entre os parênteses na hora do uso.
$ gcc -std=c23 -Wall -Wextra quadrado.c -o quadrado && ./quadrado QUADRADO(4) = 16 QUADRADO(2+3) = 11 « 2+3 é 5, e 5 ao quadrado é 25, não 11!
Nenhum erro, nenhum aviso, e uma resposta errada. Este é o bug mais famoso das macros. O gcc -E -P mostra o motivo. Num arquivo pequeno, sem #include, o resultado fica curto:
#define QUADRADO(x) x * x #define QUADRADO2(x) (x) * (x) #define QUADRADO3(x) ((x) * (x)) int a = QUADRADO(2+3); int b = 100 / QUADRADO2(5); int c = 100 / QUADRADO3(5);
$ gcc -E -P mini.c int a = 2+3 * 2+3; « o texto "2+3" entrou em cada x int b = 100 / (5) * (5); int c = 100 / ((5) * (5));
Olhe a primeira linha. O pré-processador não calculou 2+3: ele copiou o texto 2+3 para cada x e entregou 2+3 * 2+3 ao compilador. A multiplicação vem antes da soma (aula Precedência), então a conta é 2 + 6 + 3 = 11.
Parênteses em cada parâmetro…
O primeiro conserto é cercar cada x de parênteses, como no QUADRADO2: (2+3) * (2+3) dá 25. Mas a segunda linha da saída mostra que ainda falta algo:
#include <stdio.h>
#define QUADRADO(x) (x) * (x)
int main(void) {
printf("QUADRADO(2+3) = %d\n", QUADRADO(2+3));
printf("100 / QUADRADO(5) = %d\n", 100 / QUADRADO(5));
return 0;
}
$ gcc -std=c23 -Wall -Wextra meio.c -o meio && ./meio QUADRADO(2+3) = 25 100 / QUADRADO(5) = 100 « 5 ao quadrado é 25, e 100 / 25 é 4!
O texto virou 100 / (5) * (5). Divisão e multiplicação têm a mesma força e são feitas da esquerda para a direita: 100 / 5 = 20, depois 20 * 5 = 100.
…e em volta da macro inteira
#include <stdio.h>
#define QUADRADO(x) ((x) * (x))
int main(void) {
printf("QUADRADO(2+3) = %d\n", QUADRADO(2+3));
printf("100 / QUADRADO(5) = %d\n", 100 / QUADRADO(5));
return 0;
}
$ gcc -std=c23 -Wall -Wextra certo.c -o certo && ./certo QUADRADO(2+3) = 25 100 / QUADRADO(5) = 4
((x) * (x)), ((a) + (b)). Os de dentro protegem contra o que entra; os de fora protegem contra o que está em volta. Parece exagero, e é de propósito.
Missão: rode e veja os dois resultados errados. Depois conserte só a linha do #define, com a regra dos parênteses, até aparecer 25 e 4.
#include <stdio.h>
#define QUADRADO(x) x * x
int main(void) {
printf("QUADRADO(2+3) = %d\n", QUADRADO(2+3));
printf("100 / QUADRADO(5) = %d\n", 100 / QUADRADO(5));
return 0;
}
#include <stdio.h>
#define QUADRADO(x) ((x) * (x))
int main(void) {
printf("QUADRADO(2+3) = %d\n", QUADRADO(2+3));
printf("100 / QUADRADO(5) = %d\n", 100 / QUADRADO(5));
return 0;
}
2. O efeito duplo: o argumento roda duas vezes
Os parênteses resolvem a conta, mas não resolvem tudo. Na macro ((x) * (x)), o x aparece duas vezes. Então o que você escrever ali será copiado, e executado, duas vezes. Com um número, tanto faz. Com algo que muda uma variável, não:
#include <stdio.h>
#define QUADRADO(x) ((x) * (x))
int main(void) {
int i = 3;
int q = QUADRADO(i++);
printf("q = %d, i = %d\n", q, i);
return 0;
}
$ gcc -std=c23 -Wall -Wextra duplo.c -o duplo && ./duplo
duplo.c: In function ‘main’:
duplo.c:7:23: warning: operation on ‘i’ may be undefined [-Wsequence-point]
7 | int q = QUADRADO(i++);
| ^
duplo.c:3:29: note: in definition of macro ‘QUADRADO’
3 | #define QUADRADO(x) ((x) * (x))
| ^
q = 12, i = 5 « era para ser q = 9 e i = 4
A linha 7 virou int q = ((i++) * (i++));. O i++ (aula ++ e --) aparece duas vezes, então o i subiu duas vezes: de 3 para 5. E pior: mudar a mesma variável duas vezes na mesma conta é comportamento indefinido em C (aula Comportamento Indefinido): a norma não diz qual resultado sai. O gcc deu 12; outro compilador pode dar outra coisa. Por isso o aviso: "a operação em i pode ser indefinida". O Clang do editor avisa a mesma coisa com outras palavras: multiple unsequenced modifications to 'i'.
Mesmo sem ++, o efeito duplo aparece com funções. A macro MAIOR devolve o maior de dois números, e a Ana passa a ela uma função que lê uma nota:
#include <stdio.h>
#define MAIOR(a, b) ((a) > (b) ? (a) : (b))
int ler_nota(void) {
printf(" (lendo a nota...)\n");
return 8;
}
int main(void) {
int m = MAIOR(ler_nota(), 5);
printf("Maior: %d\n", m);
return 0;
}
$ gcc -std=c23 -Wall -Wextra maior.c -o maior && ./maior
(lendo a nota...)
(lendo a nota...) « a função rodou duas vezes!
Maior: 8
$ gcc -E -P maior.c | tail -4
int m = ((ler_nota()) > (5) ? (ler_nota()) : (5));
printf("Maior: %d\n", m);
return 0;
}
Repare: a macro tem dois parâmetros, separados por vírgula, e usa o operador ternário (aula Operador Ternário). Como a aparece duas vezes no texto, ler_nota() é chamada uma vez para comparar e outra para devolver o valor. Se a função lesse o teclado, a Ana teria de digitar a nota duas vezes. E desta vez nenhum aviso apareceu.
i++, x = 5, scanf(…), uma função que imprime ou lê. Passe só nomes de variáveis e contas simples. Como ninguém lembra disso o tempo todo, a solução de verdade é usar uma função (seção 6).
3. #: transformar o argumento em texto
Dentro de uma macro, um # antes do nome do parâmetro transforma o argumento em um texto entre aspas. O nome técnico é stringification, "transformar em string". Com isso, a Ana cria uma macro que mostra o nome da variável e o valor dela, ótima para depurar:
#include <stdio.h>
#define MOSTRA(x) printf(#x " = %d\n", x)
int main(void) {
int cadernos = 3;
int preco = 12;
MOSTRA(cadernos);
MOSTRA(preco);
MOSTRA(cadernos * preco);
return 0;
}
$ gcc -std=c23 -Wall -Wextra mostra.c -o mostra && ./mostra
cadernos = 3
preco = 12
cadernos * preco = 36
$ gcc -E -P mostra.c | tail -6
int preco = 12;
printf("cadernos" " = %d\n", cadernos);
printf("preco" " = %d\n", preco);
printf("cadernos * preco" " = %d\n", cadernos * preco);
return 0;
}
- #x
- Vira o texto do argumento entre aspas:
"cadernos","cadernos * preco". - " = %d\n"
- Um segundo texto, logo ao lado.
- x
- O argumento normal, sem
#: vira o valor que oprintfmostra.
E como "cadernos" " = %d\n" funciona no printf? Em C, dois textos entre aspas lado a lado, separados só por espaço, são colados pelo compilador num texto só: "cadernos = %d\n". Isso vale em qualquer lugar do código, não só em macros.
Experimente: rode. Depois acrescente MOSTRA(preco * 2); e MOSTRA(cadernos + 1); e veja a conta aparecer escrita na saída, do jeito que você digitou.
#include <stdio.h>
#define MOSTRA(x) printf(#x " = %d\n", x)
int main(void) {
int cadernos = 3;
int preco = 12;
MOSTRA(cadernos);
MOSTRA(preco);
MOSTRA(cadernos * preco);
return 0;
}
4. ##: colar dois pedaços num nome só
O ## cola o que está à esquerda com o que está à direita, formando uma palavra só. O nome técnico é token pasting, "colar pedaços":
#include <stdio.h>
#define PRECO(item) preco_##item
int main(void) {
int preco_caderno = 12;
int preco_lapis = 2;
printf("Caderno: %d\n", PRECO(caderno));
printf("Lápis: %d\n", PRECO(lapis));
return 0;
}
$ gcc -std=c23 -Wall -Wextra cola.c -o cola && ./cola Caderno: 12 Lápis: 2
PRECO(caderno) virou preco_caderno, o nome da variável. Sem o ##, preco_item seria uma palavra só e o item nem seria trocado. Você vai ver ## em bibliotecas que geram várias funções parecidas de uma vez (uma para int, outra para double…). No dia a dia, use com moderação: um nome que só aparece depois do pré-processamento é difícil de achar com a busca do editor.
5. Macros de várias linhas e o do { } while (0)
Uma diretiva termina no fim da linha. Para continuar na linha de baixo, termine a linha com uma barra invertida \. Ela tem de ser o último caractere da linha (nem um espaço depois).
A Ana quer uma macro VENDER que mostra a venda e soma 1 no contador. São dois comandos. A primeira tentativa:
#include <stdio.h>
int vendas = 0;
#define VENDER(item) printf("Vendido: %s\n", item); vendas++
int main(void) {
int tem_estoque = 0;
if (tem_estoque)
VENDER("caderno");
printf("Vendas: %d\n", vendas);
return 0;
}
$ gcc -std=c23 -Wall -Wextra conta_ruim.c -o conta_ruim && ./conta_ruim
conta_ruim.c: In function ‘main’:
conta_ruim.c:4:22: warning: macro expands to multiple statements [-Wmultistatement-macros]
4 | #define VENDER(item) printf("Vendido: %s\n", item); vendas++
| ^~~~~~
conta_ruim.c:9:9: note: in expansion of macro ‘VENDER’
9 | VENDER("caderno");
| ^~~~~~
conta_ruim.c:8:5: note: some parts of macro expansion are not guarded by this ‘if’ clause
8 | if (tem_estoque)
| ^~
Vendas: 1 « não vendeu nada, mas contou uma venda!
Sem estoque, nada foi vendido, mas o contador subiu. O if sem chaves comanda só um comando (aula if e else). Depois da troca, o código ficou assim:
if (tem_estoque)
printf("Vendido: %s\n", "caderno");
vendas++; // fora do if: roda sempre
O gcc percebeu e avisou: "a macro se expande em vários comandos" e "algumas partes da expansão não estão protegidas por este if". O Clang do editor não dá esse aviso: lá, o programa simplesmente mostra Vendas: 1.
Chaves resolvem? Quase.
A ideia natural é pôr chaves em volta: { printf(…); vendas++; }. Agora o bloco todo fica dentro do if. Mas veja o que acontece com um else, nesta macro que troca dois valores:
#include <stdio.h>
#define TROCA(a, b) { int tmp = a; a = b; b = tmp; }
int main(void) {
int x = 1, y = 2;
if (x < y)
TROCA(x, y);
else
printf("Já estava em ordem\n");
printf("x = %d, y = %d\n", x, y);
return 0;
}
$ gcc -std=c23 -Wall -Wextra troca_ruim.c -o troca_ruim
troca_ruim.c: In function ‘main’:
troca_ruim.c:9:5: error: ‘else’ without a previous ‘if’
9 | else
| ^~~~
"else sem um if antes." O culpado é o ; que você escreve depois de TROCA(x, y), como em qualquer comando. O texto ficou if (x < y) { … } ; else. O if termina no }, o ; é um comando vazio, e o else fica sem par. (No editor, o Clang diz só error: expected expression, na mesma linha.)
O truque: do { … } while (0)
Os programadores de C resolveram isso com um laço que roda exatamente uma vez. O do { } while (0) (aula do while) junta vários comandos num só e exige o ; no final, como um comando comum:
#include <stdio.h>
int vendas = 0;
#define VENDER(item) \
do { \
printf("Vendido: %s\n", item); \
vendas++; \
} while (0)
int main(void) {
int tem_estoque = 0;
if (tem_estoque)
VENDER("caderno");
else
printf("Sem estoque!\n");
printf("Vendas: %d\n", vendas);
return 0;
}
- \
- No fim de cada linha: "a macro continua na linha de baixo". O alinhamento das barras é só para ficar bonito.
- do {
- Abre o bloco com os comandos da macro.
- } while (0)
- "Repita enquanto 0": 0 é falso, então o bloco roda uma vez e acaba. Sem
;aqui: quem põe o;é quem usa a macro.
$ gcc -std=c23 -Wall -Wextra conta_boa.c -o conta_boa && ./conta_boa Sem estoque! Vendas: 0
Sem aviso, sem erro, e o contador certo. O texto agora é if (tem_estoque) do { … } while (0); else …: o do … while (0); é um comando, o if comanda tudo e o else encontra o seu par. O compilador some com o laço na hora de gerar o programa; ele não custa nada.
Macros com número variável de argumentos C99
Uma macro pode receber "quantos argumentos vierem", com ... na lista e __VA_ARGS__ no texto (variable arguments, "argumentos variáveis"). Antes, um uso que mostra por que macros ainda existem. A Ana quer mensagens de depuração que digam a linha de onde vieram:
#include <stdio.h>
#define LOG(msg) fprintf(stderr, "[%s:%d] %s\n", __FILE__, __LINE__, msg)
int main(void) {
int estoque = 2;
LOG("Programa começou");
if (estoque < 5)
LOG("Estoque baixo!");
return 0;
}
$ gcc -std=c23 -Wall -Wextra log.c -o log && ./log [log.c:7] Programa começou [log.c:9] Estoque baixo!
Cada mensagem mostra a sua própria linha, porque o __LINE__ foi carimbado no lugar onde o LOG foi usado. Uma função log mostraria sempre a linha de dentro dela. O fprintf(stderr, …) escreve na saída de erros (aula stdin, stdout e stderr).
Para o LOG aceitar um formato com valores, como o printf, use .... O __VA_OPT__(,) C23 põe a vírgula só quando vieram valores extras:
#include <stdio.h>
#define LOG(fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__ __VA_OPT__(,) __VA_ARGS__)
int main(void) {
int estoque = 2;
LOG("Programa começou");
LOG("Estoque baixo: %d unidades de %s", estoque, "caderno");
return 0;
}
$ gcc -std=c23 -Wall -Wextra logv.c -o logv && ./logv [logv.c:8] Programa começou [logv.c:9] Estoque baixo: 2 unidades de caderno
No segundo LOG, fmt é o texto com os %, e __VA_ARGS__ é estoque, "caderno". Antes do C23 não existia __VA_OPT__, e cada compilador tinha o seu truque (o gcc aceita , ##__VA_ARGS__). Com -std=c17 -pedantic, o gcc avisa: ‘__VA_OPT__’ is not available until C23. Funções com número variável de argumentos de verdade têm aula própria: Funções Variádicas.
6. Macro ou função inline?
Quase tudo o que uma macro calcula, uma função calcula melhor. A palavra inline C99 sugere ao compilador "copie o código desta função no lugar da chamada", que era a vantagem de velocidade das macros. E o static diz que ela é só deste arquivo (aula static e extern). Veja a mesma conta como função:
#include <stdio.h>
static inline int quadrado(int x) {
return x * x;
}
int main(void) {
int i = 3;
printf("quadrado(2+3) = %d\n", quadrado(2+3));
int q = quadrado(i++);
printf("q = %d, i = %d\n", q, i);
return 0;
}
$ gcc -std=c23 -Wall -Wextra inline.c -o inline && ./inline quadrado(2+3) = 25 q = 9, i = 4
Sem um parêntese a mais, quadrado(2+3) dá 25: a função recebe o valor 5, e não o texto. E quadrado(i++) dá 9 com i valendo 4: o argumento é calculado uma vez só. A função também confere o tipo. Passe um texto para a macro e para a função e compare as mensagens:
$ gcc -std=c23 -Wall -Wextra tipo.c -o tipo
tipo.c: In function ‘main’:
tipo.c:3:26: error: invalid operands to binary * (have ‘char *’ and ‘char *’)
3 | #define QUADRADO(x) ((x) * (x))
| ~ ^
| | |
| | char *
| char *
tipo.c:6:20: note: in expansion of macro ‘QUADRADO’
6 | printf("%d\n", QUADRADO("oi"));
| ^~~~~~~~
Com a macro, o erro aponta para dentro do #define (linha 3), com uma nota dizendo onde ela foi usada. Com a função quadrado("oi"), o gcc diz direto na linha da chamada: passing argument 1 of ‘quadrado’ makes integer from pointer without a cast ("passar o argumento 1 de quadrado transforma um ponteiro em inteiro"). Num programa grande, a diferença é enorme.
| Use… | Quando |
|---|---|
Função (com static inline se for pequena e usada só no arquivo) | Para calcular algo: contas, comparações, conversões. É a escolha padrão. |
| Macro com parâmetros | Quando precisa de algo que só o pré-processador faz: # (nome da variável como texto), ##, __FILE__/__LINE__ do lugar de uso, ou servir para vários tipos ao mesmo tempo (para isso o C11 também tem _Generic). |
Mais sobre inline na aula Funções inline.
Quando dá errado
error: ‘x’ undeclared apontando para dentro do #define
$ gcc -std=c23 -Wall -Wextra espaco.c -o espaco
espaco.c: In function ‘main’:
espaco.c:3:19: error: ‘x’ undeclared (first use in this function)
3 | #define QUADRADO (x) ((x) * (x))
| ^
espaco.c:6:20: note: in expansion of macro ‘QUADRADO’
6 | printf("%d\n", QUADRADO(4));
| ^~~~~~~~
« … a mesma nota repetida …
O que significa: há um espaço entre QUADRADO e (x). Com o espaço, o pré-processador entende uma macro sem parâmetros, cujo texto é (x) ((x) * (x)). Aí QUADRADO(4) vira (x) ((x) * (x))(4), e o x não existe.
Como consertar: o parêntese dos parâmetros vem colado no nome: #define QUADRADO(x) …. No uso, tanto faz: QUADRADO (4) funciona.
error: macro ‘QUADRADO’ passed 2 arguments, but takes just 1
$ gcc -std=c23 -Wall -Wextra args.c -o args
args.c: In function ‘main’:
args.c:6:33: error: macro ‘QUADRADO’ passed 2 arguments, but takes just 1
6 | printf("%d\n", QUADRADO(3, 4));
| ^
args.c:3:9: note: macro ‘QUADRADO’ defined here
3 | #define QUADRADO(x) ((x) * (x))
| ^~~~~~~~
« … e um erro ‘QUADRADO’ undeclared, consequência do primeiro …
O que significa: "a macro recebeu 2 argumentos, mas aceita só 1". A vírgula separa argumentos, como numa função.
Como consertar: passe o número certo de argumentos. Se o argumento precisa ter uma vírgula de verdade dentro, ponha-o entre parênteses: MOSTRA((a, b)).
O que significa: quase sempre, faltam parênteses na macro (QUADRADO(2+3) dando 11) ou um argumento foi calculado duas vezes (uma função chamada duas vezes, como em MAIOR(ler_nota(), 5)).
Como consertar: rode gcc -E -P arquivo.c e leia o texto que o compilador recebeu. Aplique a regra dos parênteses. Se o argumento tem efeito (++, função), guarde o valor numa variável antes, ou troque a macro por uma função.
warning: macro expands to multiple statements / error: ‘else’ without a previous ‘if’
O que significa: uma macro com vários comandos foi usada num if sem chaves. Sem chaves na macro, só o primeiro comando fica dentro do if. Com chaves mas sem do … while (0), o ; depois do uso separa o if do else.
Como consertar: escreva a macro como do { … } while (0), sem ; no final. E, de quebra, use sempre chaves nos seus if.
warning: backslash and newline separated by space
O que significa: numa macro de várias linhas, há um espaço depois da \. O gcc avisa ("barra invertida e quebra de linha separadas por espaço") e aceita assim mesmo, mas outros compiladores podem terminar a macro ali.
Como consertar: apague os espaços depois da \. Ela tem de ser o último caractere da linha.
Referência
| Escrita | O que faz |
|---|---|
#define F(x) ((x) * 2) | Macro com um parâmetro (parênteses colados no nome) |
#define F(a, b) ((a) + (b)) | Macro com dois parâmetros |
#x | Transforma o argumento em texto: MOSTRA(preco) → "preco" |
a##b | Cola dois pedaços num nome: preco_##item → preco_caderno |
\ no fim da linha | A macro continua na linha de baixo |
do { … } while (0) | Junta vários comandos num só, seguro em if/else |
#define F(fmt, ...) + __VA_ARGS__ | Número variável de argumentos C99 |
__VA_OPT__(,) | Põe a vírgula só se vieram argumentos extras C23 |
static inline int f(int x) { … } | A alternativa com tipo e cálculo único |
gcc -E -P arquivo.c | Mostra o texto depois das trocas |
Exercícios
1. Parênteses
Complete a macro DOBRO com a regra dos parênteses:
2. Nome como texto
Complete para que NOME(idade) vire o texto "idade":
3. Com #define DOBRO(n) n * 2, quanto vale DOBRO(1+4)?
4. Com #define MAIOR(a, b) ((a) > (b) ? (a) : (b)), quantas vezes ler() pode ser chamada em MAIOR(ler(), 0)?
5. Por que as macros de vários comandos usam do { … } while (0)?
6. A Ana quer calcular a média de duas notas. Qual é a melhor escolha?
Missão final: o caixa da lojinha
Missão: o programa não compila: a macro VENDER tem dois comandos e quebra o if/else. Reescreva só a macro com do { … } while (0) (use \ no fim das linhas, ou escreva tudo numa linha só). A saída deve ficar assim, sem avisos:
Vendido: lápis Sem estoque: caderno Vendas: 1
#include <stdio.h>
int vendas = 0;
#define VENDER(item) printf("Vendido: %s\n", item); vendas++
void atender(const char *item, int estoque) {
if (estoque > 0)
VENDER(item);
else
printf("Sem estoque: %s\n", item);
}
int main(void) {
atender("lápis", 5);
atender("caderno", 0);
printf("Vendas: %d\n", vendas);
return 0;
}
#include <stdio.h>
int vendas = 0;
#define VENDER(item) \
do { \
printf("Vendido: %s\n", item); \
vendas++; \
} while (0)
void atender(const char *item, int estoque) {
if (estoque > 0)
VENDER(item);
else
printf("Sem estoque: %s\n", item);
}
int main(void) {
atender("lápis", 5);
atender("caderno", 0);
printf("Vendas: %d\n", vendas);
return 0;
}
Resumo
- Uma macro com parâmetros (
#define F(x) …, parêntese colado no nome) é um carimbo: o pré-processador copia o texto do argumento em cada lacuna. Não calcula e não confere o tipo. - Regra dos parênteses: em volta de cada parâmetro e da macro inteira,
((x) * (x)). Sem eles,QUADRADO(2+3)dá 11 e100 / QUADRADO(5)dá 100. - Efeito duplo: o argumento roda tantas vezes quantas aparece. Nunca passe
i++ou funções com efeito para uma macro. #xtransforma o argumento em texto;a##bcola dois pedaços num nome.- Macro com vários comandos:
\no fim das linhas edo { … } while (0), sem;no fim. ...e__VA_ARGS__(C99) aceitam vários argumentos;__VA_OPT__(C23) resolve a vírgula.- Para calcular, prefira uma função (
static inline). Deixe as macros para o que só o pré-processador faz. Para enxergar qualquer macro:gcc -E -P.