Última actualización:2026-10-12
Macros y compilación condicional en C++¶
Sobre el autor:¶
Cuenta pública personal:

1. Símbolos especiales en macros¶
Se dividen en varias categorías: #, ##, \
1.1 Operador de conversión a cadena (#)¶
Cuando se usa un # delante de un parámetro dentro de una macro, el preprocesador convierte ese parámetro en un arreglo de caracteres, dicho de otro modo: # significa "conversiones a cadena"; el # que aparece en la definición de una macro convierte en una cadena el parámetro que lo sigue.
Nota: solo puede usarse en definiciones de macros con parámetros de entrada, y debe colocarse delante del nombre del parámetro dentro del cuerpo de la definición de la macro.
Por ejemplo:
#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;
}
El código anterior muestra el uso básico y las reglas de tratamiento de los espacios, que son las siguientes:
- Se ignoran los espacios delante y detrás del nombre del parámetro pasado.
Salida:
- Cuando hay espacios entre los nombres de los parámetros pasados, el compilador concatena automáticamente las subcadenas, uniendo cada subcadena con un solo espacio e ignorando los espacios restantes.
Salida:
1.2 Operador de concatenación de símbolos (##)¶
"##" es una forma de conexión mediante separación: primero separa y luego realiza una concatenación forzada. Convierte los múltiples parámetros formales de la definición de la macro en un único nombre de parámetro real.
Puntos a tener en cuenta:
(1) Al conectar parámetros formales con ##, los espacios antes y después de ## son opcionales.
(2) El nombre de parámetro real resultante de la conexión debe ser un nombre de parámetro realmente existente o una definición de macro conocida por el compilador.
(3) Si el parámetro posterior a ## es a su vez una macro, ## impide la expansión de esa macro.
Ejemplo:
#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 continuación de línea ()¶
Cuando una macro definida no puede expresarse por completo en una sola línea, se puede usar "\" para indicar que la definición de la macro continúa en la línea siguiente.
Nota: deje un espacio antes de .
El código anterior puede verse en: sig_examp.cpp
2. Uso de do{...}while(0)¶
2.1 Evitar interpretaciones semánticas erróneas¶
Por ejemplo:
Esta macro, una vez expandida, queda así:
La intención era que con a>0 se ejecutaran f1 y f2, pero en realidad f2 se ejecuta cada vez, de modo que el resultado es erróneo.
Para resolver este tipo de problemas, al escribir código normalmente se puede emplear un bloque {}.
Por ejemplo:
Sin embargo, se observa que tras la expansión de la macro anterior sobra un punto y coma, y la sintaxis real no es del todo correcta. (Aunque compila y se ejecuta sin problema; lo normal es que no haya punto y coma).
2.2 Evitar el uso de goto para el control de flujo¶
En algunas funciones puede que necesitemos hacer algún trabajo de limpieza antes de la sentencia return, por ejemplo liberar el espacio de memoria solicitado con malloc al comienzo de la función; usar goto siempre ha sido un método sencillo:
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;
}
Pero dado que goto no se ajusta a la estructuración de la ingeniería del software y puede hacer que el código resulte difícil de entender, mucha gente desaconseja su uso; en ese caso podemos usar do{...}while(0) para hacer lo mismo:
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;
}
Aquí el cuerpo principal de la función se envuelve con do{...}while(0), se usa break en lugar de goto y el trabajo de limpieza posterior se coloca después del while; de este modo se consigue el mismo efecto y, además, la legibilidad y la mantenibilidad del código son mucho mejores que las del código con goto anterior.
2.3 Evitar las advertencias causadas por macros¶
En el kernel, debido a las limitaciones de las distintas arquitecturas, muchas veces se utilizan macros vacías. Al compilar, estas macros vacías generan warnings; para evitar tales warnings, podemos usar do{...}while(0) para definir macros vacías:
2.4 Definir un único bloque de función para completar operaciones complejas¶
Si tienes una función compleja, con muchas variables, y no quieres añadir una función nueva, puedes usar do{...}while(0) y escribir tu código dentro; dentro puedes definir variables sin preocuparte de que sus nombres colisionen con los de antes o después de la función. Esta situación se refiere a cuando una variable se usa en varios lugares (pero con un significado distinto en cada uno); podemos reducir el ámbito dentro de cada do-while, por ejemplo:
int fc()
{
int k1 = 10;
cout<<k1<<endl;
do{
int k1 = 100;
cout<<k1<<endl;
}while(0);
cout<<k1<<endl;
}
El código anterior puede verse en: do_while.cpp
Artículo de estudio: https://www.cnblogs.com/lizhenghn/p/3674430.html
Temas relacionados¶
- Funciones virtuales puras y clases abstractas en C++ | C++那些事
- Aserciones assert en C++ | C++那些事
- Campos de bits en C++ | C++那些事
- Implementar el polimorfismo de C++ en lenguaje C | C++那些事
- const en C++: constantes y modificación | C++那些事
- La familia completa de const en C++ | C++那些事
- El operador de desreferencia * en C++ | C++那些事
- Inicialización y asignación en C++ | C++那些事
- Formas de devolver varios valores en C++ | C++那些事
- La palabra clave mutable en C++ | C++那些事