C语言宏嵌套
在C语言的宏中是容许嵌套的,其嵌套后,一般的展开规律像函数的参数一样,先展开参数,再分析函数,所以展开顺序是由内而外,但是当宏中有#则不再展开参数了,如果宏中有##,则先展开函数,再展开里面的参数。
如下面的例子:
#include <stdio.h>
#define TO_STRING2( x ) #x
#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x
#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x
int main()
{
const char * str = TO_STRING(PARAM( ADDPARAM( 1 ) ) );
printf("%s\n",str);
str = TO_STRING2(PARAM( ADDPARAM( 1 ) ) );
printf("%s\n",str);
return 0;
}
它的输出结果为:
"ADDPARAM( 1 )"
PARAM( ADDPARAM( 1 ) )
对于宏TO_STRING,它的定义中没有#,所以先展开里面的“PARAM( ADDPARAM( 1 ) )”,由于PARAM中有#,所以里面展开的结果为ADDPARAM( 1 ),然后外面再展开,其结果为"ADDPARAM( 1 )"
而对于TO_STRING2,其定义中有#,所以直接展开,其结果为PARAM( ADDPARAM( 1 ) )
去掉include的预处理后文件为:
/* 6 */ int main ( )
/* 7 */ {
/* 8 */ const char * str = "\"ADDPARAM( 1 )\"" ;
/* 9 */ printf ( "%s\n" , str ) ;
/* 10 */ str = "PARAM( ADDPARAM( 1 ) )" ;
/* 11 */ printf ( "%s\n" , str ) ;
/* 12 */ return 0 ;
/* 13 */ }
注:\"为转义字符反斜杠表示的双引号。
而对于下面的例子:
#include <stdio.h>
#define TO_STRING2( x ) a_##x
#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x
#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x
int main()
{
const char *str = TO_STRING(TO_STRING2(PARAM( ADDPARAM( 1 ) ) ));
printf("%s\n",str);
return 0;
}
其输出结果为:
因为首先分析TO_STRING的参数TO_STRING2(PARAM( ADDPARAM( 1 ) ) ),而对于TO_STRING2(x),由于其定义中有##,所以先展开该函数,其结果为a_PARAM(ADDPARAM( 1 )),而ADDPARAM( 1 )展开,结果为INT_1,所以其总结果为a_PARAM( INT_1 )
/* 6 */ int main ( )
/* 7 */ {
/* 8 */ const char * str = "a_PARAM( INT_1)" ;
/* 9 */ printf ( "%s\n" , str ) ;
/* 10 */ return 0 ;
/* 11 */ }
【引用结束】
【总结】
先展开宏,再展开宏参数,这种说法不太妥当,我总结的宏处理过程如下:
遇到宏名后(1)
- 检查对应的宏体中是否含有#和##运算符
- 无——处理宏参数(实参)(2)
- 遇到宏名,回到(1)
- 没有遇到,在宏体中用实参字符串替换形式参数,再检查是否遇到宏名(4)
- 有——不检查宏参数,在宏体中用实参字符串替换形式参数,再检查是否含有宏名(3)
详细地解释一下整个过程并做验证
【经典题过程】
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
h(f(1,2)) (2) (1)(3)(5)
h(12) (4)
g(12) (1)(3)(5)
"12"
g(f(1,2)) (3)
"f(1,2)" // 受字符串符号屏蔽
【对于那个讨论帖的问题】
#define cat(a,b) a ## b
#define f(a) fff a
#define ab AB
cat(cat(1,2),3)
cat(1,2)3
cat(a,b)
ab
AB
f(cat(cat(1,2),3))
fff cat(cat(1,2),3)
fff cat(1,2)3
【例子1过程】
遇到宏名后(1)
检查对应的宏体中是否含有#和##运算符
无——处理宏参数(实参)(2)
遇到宏名,回到(1)
没有遇到,在宏体中用实参字符串替换形式参数,再检查是否遇到宏名(4)
...
有——不检查宏参数,在宏体中用实参字符串替换形式参数,再检查是否含有宏名(3)
遇到宏名,回到(1)
没有遇到,结束(5)
#define TO_STRING2( x ) #x
#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x
#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x
TO_STRING(PARAM( ADDPARAM( 1 ) ) )
TO_STRING("ADDPARAM( 1 )" )
TO_STRING1("ADDPARAM( 1 )" )
"\"ADDPARAM( 1 )\""
TO_STRING2(PARAM( ADDPARAM( 1 ) ) )
"PARAM( ADDPARAM( 1 ) )"
【例子2过程】
#define TO_STRING2( x ) a_##x
#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x
#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x
TO_STRING(TO_STRING2(PARAM( ADDPARAM( 1 ) ) ))
TO_STRING(a_PARAM( ADDPARAM( 1 ) ))
TO_STRING(a_PARAM( INT_1 ))
TO_STRING1(a_PARAM( INT_1 ))
"a_PARAM( INT_1 )"
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。