①一个较长的宏定义可以分成若干行,这需要在待续的行末尾加上一个反斜杠符”\”
#define TUP_ASSERT(__expression) do {\
if (!( __expression )) \
{ \
SYSLOG_LEGACY(EAaSysLogSeverityLevel_Error,"<TUP ASSERTION FAILED> (%s) file: %s line:%u", #__expression,__FILE__, __LINE__); \
AaErrorAssertion(#__expression,__FILE__, __LINE__); \
} \
} while(0)
②在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串
#define dprint(expr) printf(#expr “ =%g\n”, expr)
使用语句dprint(x/y),该宏将被扩展为
printf(“x/y” “= %g\n”, x/y);
③预处理运算符##为宏展开提供参数连接的作用
#define paste(front, back) front ##back
宏调用paste(name,1)的结果将建立记号name1
④宏展开顺序大致可以归结为:
第一步:首先用实参代替形参,将实参代入宏文本中
第二步:如果实参也是宏,则展开实参
第三步:最后继续处理宏替换后的宏文本,如果仍包含宏,则继续展开
注意:如果在第二步,实参代入宏文本后,实参之前或之后遇到#或##,实参不再展开
例1:
#define cat(a,b) a ## b
宏调用:cat(cat(1, 2), 3) 的展开顺序为:
cat(cat(1, 2), 3) -->cat(1, 2) ## 3 -->cat(1, 2)3
cat(1,2)仍是宏,但后面是##,不再展开,结果为:cat(1, 2)3
例2:
#define cat(a,b) a ## b
#define xcat(x, y) cat(x, y)
宏调用 xcat(xcat(1, 2), 3) 的展开顺序为:
xcat(xcat(1,2), 3) -->cat(xcat(1, 2), 3) -->cat(cat(1, 2), 3) -->cat(1 ## 2, 3) --> 1 ##2 ## 3 -->123
务必注意参数的处理顺序,第二步在第三步前面执行,第一层宏展开后,实参是宏,则首先处理实参的宏展开,即使宏替换后本身也是宏。在例2中,虽然也生成的cat(cat(1, 2), 3),但是是首先执行里面的cat(1, 2), 所以结果不一样。例2中以下顺序是错的:
xcat(xcat(1,2), 3) --> cat(xcat(1, 2), 3) --> xcat(1, 2) ## 3 -->xcat(1, 2)3
在Linux测试结果:
使用gcc –E 编译可以只做预处理:源文件hepeng.c:
*****************************************************************************
#define cat(a,b) a ## b
#define xcat(x,y) cat(x,y)
int main()
{
cat(cat(1,2),3);
xcat(xcat(1,2),3);
xcat(cat(1,2),3);
return 0;
}
*****************************************************************************
[penhe@hzling23 program]$gcc -E hepeng.c |more
*****************************************************************************
# 1 "hepeng1.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "hepeng1.c"
int main()
{
cat(1,2)3;
123;
123;
return 0;
}
*****************************************************************************