Última atualização:2026-10-12
Macros e compilação condicional em C++¶
Sobre o autor:¶
Conta oficial do WeChat:

1.Símbolos especiais em macros¶
Dividem-se em vários tipos: #, ##, \
1.1 Operador de stringificação (#)¶
Ao usar um # antes de um parâmetro em uma macro, o pré-processador converte esse parâmetro em um array de caracteres; em outras palavras: # significa "transformar em string"; o # que aparece na definição de uma macro converte o parâmetro que o segue em uma string.
Nota: ele só pode ser usado em macros com parâmetros de entrada e deve ser colocado antes do nome do parâmetro no corpo da definição da macro.
Por exemplo:
#define exp(s) printf("test s is:%s\n",s)
#define exp1(s) printf("test s is:%s\n",#s)
#define exp2(s) #s
int main() {
exp("hello");
exp1(hello);
string str = exp2( bac );
cout<<str<<" "<<str.size()<<endl;
/**
* 忽略传入参数名前面和后面的空格。
*/
string str1 = exp2( asda bac );
/**
* 当传入参数名间存在空格时,编译器将会自动连接各个子字符串,
* 用每个子字符串之间以一个空格连接,忽略剩余空格。
*/
cout<<str1<<" "<<str1.size()<<endl;
return 0;
}
O código acima mostra o uso básico e as regras de tratamento de espaços; as regras de tratamento de espaços são as seguintes:
- Espaços antes e depois do nome do parâmetro de entrada são ignorados.
Saída:
- Quando há espaços entre os nomes dos parâmetros de entrada, o compilador conecta automaticamente as subcadeias, unindo-as com um único espaço entre cada uma e ignorando os espaços excedentes.
Saída:
1.2 Operador de concatenação de símbolos (##)¶
O "##" é uma forma de conexão por separação: sua função é primeiro separar e depois forçar a conexão, transformando múltiplos parâmetros formais da definição da macro em um único nome de parâmetro real.
Pontos de atenção:
(1) Ao conectar parâmetros formais com ##, os espaços antes e depois do ## são opcionais.
(2) O nome de parâmetro real resultante da conexão deve ser um nome de parâmetro realmente existente ou uma definição de macro já conhecida pelo compilador.
(3) Se o parâmetro após o ## for ele próprio uma macro, o ## impede a expansão dessa macro.
Exemplo:
#define expA(s) printf("前缀加上后的字符串为:%s\n",gc_##s) //gc_s必须存在
// 注意事项2
#define expB(s) printf("前缀加上后的字符串为:%s\n",gc_ ## s) //gc_s必须存在
// 注意事项1
#define gc_hello1 "I am gc_hello1"
int main() {
// 注意事项1
const char * gc_hello = "I am gc_hello";
expA(hello);
expB(hello1);
}
1.3 Operador de continuação de linha ()¶
Quando a macro definida não cabe completa em uma linha, pode-se usar "\" para indicar que a definição da macro continua na linha seguinte.
Atenção para deixar um espaço antes do .
O código acima está em: sig_examp.cpp
2.Uso de do{...}while(0)¶
2.1 Evitar distorção semântica¶
Por exemplo:
Expandida, essa macro se torna:
A intenção era executar f1 e f2 quando a>0, mas, na prática, f2 é executado sempre — eis o erro.
Para resolver esse tipo de problema, ao escrever código, geralmente pode-se usar o bloco {}.
Assim:
Mas note que, após a expansão da macro acima, sobra um ponto e vírgula, e a sintaxe real não fica muito correta. (Embora compile e execute sem problemas; o normal é não ter o ponto e vírgula.)
2.2Evitar o uso de goto no fluxo de controle¶
Em algumas funções, talvez precisemos fazer algum trabalho de limpeza antes do return, como liberar o espaço de memória alocado com malloc no início da função; usar goto sempre foi um método simples:
int f() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout<<*p<<endl;
#ifndef DEBUG
int error=1;
#endif
if(error)
goto END;
// dosomething
END:
cout<<"free"<<endl;
free(p);
return 0;
}
Mas, como o goto não segue a estruturação da engenharia de software e pode tornar o código difícil de entender, muitas pessoas desaconselham seu uso; nesse caso, podemos usar do{...}while(0) para fazer a mesma coisa:
int ff() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout<<*p<<endl;
do{
#ifndef DEBUG
int error=1;
#endif
if(error)
break;
//dosomething
}while(0);
cout<<"free"<<endl;
free(p);
return 0;
}
Aqui, a parte principal da função é envolvida por do{...}while(0), usando break no lugar de goto, com o trabalho de limpeza depois do while: obtém-se o mesmo efeito, e a legibilidade e a manutenibilidade do código ficam muito melhores que as do código com goto acima.
2.3 Evitar avisos causados por macros¶
No kernel, devido a limitações das diferentes arquiteturas, macros vazias são usadas com frequência. Na compilação, essas macros vazias geram warnings; para evitá-los, podemos usar do{...}while(0) para definir macros vazias:
2.4 Definir um único bloco de função para realizar operações complexas¶
Se você tem uma função complexa, com muitas variáveis, e não quer criar uma função nova, pode usar do{...}while(0), escrevendo o seu código dentro dele, onde é possível definir variáveis sem se preocupar com conflitos de nome com variáveis de antes ou de depois na função. Esse caso refere-se a usar a mesma variável em vários pontos (com significados diferentes em cada um); podemos restringir o escopo em cada do-while, por exemplo:
int fc()
{
int k1 = 10;
cout<<k1<<endl;
do{
int k1 = 100;
cout<<k1<<endl;
}while(0);
cout<<k1<<endl;
}
O código acima está em: do_while.cpp
Artigo de estudo: https://www.cnblogs.com/lizhenghn/p/3674430.html
Tópicos relacionados¶
- Funções virtuais puras e classes abstratas em C++ | C++那些事
- Asserções com assert em C++ | C++那些事
- Bit fields em C++ | C++那些事
- Implementando polimorfismo de C++ em linguagem C | C++那些事
- Constantes e modificadores com const em C++ | C++那些事
- A família completa do const em C++ | C++那些事
- O operador de desreferência * em C++ | C++那些事
- Inicialização e atribuição em C++ | C++那些事
- Formas de retornar múltiplos valores em C++ | C++那些事
- A palavra-chave mutable em C++ | C++那些事