目录
在阅读VTK和QT的源码时,遇到了类似于Python中特殊变量形式的宏,如__LINE__
和__FILE__
,这两个宏的用途是作为函数参数返回调用行数和文件名。一时惊奇,原来C里面也有这个内置宏,而且貌似在各大库的Debug模块中都经常被用到。此外也碰到了变长的宏的用法,于是查了一下标准中对宏的描述,做一下笔记,对宏代码很有帮助~
文中点击标题的角标即可转到GCC文档对应页面。
Stringizing1 (字符串化)
在宏定义中可以将传入的参数原封不动地变成字符串常量插入代码中,使用的方法是利用#
运算符。例如下面的宏
|
|
其中#EXPR
便是字符串化的参数,在编译时会变成
|
|
会输出Warning: x == 0
。这一特性可以使得使用宏定义的时候同时输出参数名字或者表达式,便于进行记录。此时若x
本身也是宏的话在#EXPR
中不会展开。如果想让宏x
在#EXPR
中也展开的话,需要再用另一个宏把这个宏包起来即可。
Concatenation2 (符号连接)
有时候向宏内传入的参数不完全是你想要的参数,或者希望通过一个参数展开成多个变量的时候,就可以使用符号连接的宏,使用方法是利用##
运算符。例如假设有一个储存命名函数的结构体
|
|
使用下面的宏可以简洁方便地定义多个结构体
|
|
其中NAME ## _command
的作用就是在NAME
展开后在末尾加上_command
,避免直接连接会导致宏无法被识别的现象。展开后得到
|
|
Variadic Macros3 (可变参数的)
可变参数的用法与普通代码中的可变参数用法是一致的,即通过对最后一个参数进行特殊声明来让这个宏可以接受变长的参数。举例如下:
|
|
这两个宏的展开效果是一样的,如果在代码中插入
|
|
则会展开成
|
|
从上面的代码可以看出,如果使用...
来表示变长参数,那么在宏定义中就用预定义宏变量__VA_ARGS__
来代表这些参数,如果是在某一个参数名的后面加上...
,那么就是用将这个参数变成变长参数。
另外,变长参数之前可以有普通的参数,如
|
|
不过需要注意的是,在标准C中这样的情况下变长参数至少需要输入一个参数,否则在转义时参数末尾会多一个逗号。即eprintf("success!\n", );
会变成fprintf(stderr, "success!\n", );
。在GNU CPP中这个问题可以通过在__VA_ARGS__
前面加上##
符号来解决。
Predefined Macros4 (预定义的宏)
在标准中有提供一些内置的宏,可以给调试提供很多方便~
__FILE__
:当前文件的路径名__LINE__
:调用处的行号__func__
(C99)/__FUNCTION__
(GCC):调用处所属的函数名__DATE__
:处理器上当前的日期__TIME__
:处理器上当前的时间__STDC_VERSION__
:C标准的版本,例如C11标准下会展开成201103__cplusplus
:在C++编译器时会被定义,展开结果同__STDC_VERSION__
其他还有很多的不在标准中的预定义的宏,具体可以查看编译器的说明。其中GNU C的预定义宏可以参考Common Predefined Macros
Directives Within Macro Arguments5 (在宏参数里修改宏)
GNU编译器还提供了在展开宏的参数时修改宏的功能。。。这个功能非常少用,先举个病态的例子:
|
|
这一段语句展开后得到的结果是1 2 1 2
,这就是在参数中修改宏。但是这个宏非常影响阅读,太tricky了,最好还是别用。