宏
宏在很多语言里都有,是一种预处理(Preprocessor)阶段的流程的能力,预处理主要知识包含
条件(conditionally)、替换(replace)、包含(include)、错误(error)、实现定义(implementation defined)、文件和行定义(file name and line information)
宏的作用主要是替换(replace)其灵活性极高可读性极差,不太推荐使用过于复杂的技巧,可以拆分为以下几点
- 定义
- 操作符
定义
宏的定义可以分为 Object-like macros 和 Function-like macros, 一种类似变量的定义一种类似方法的定义
1 | #define identifier replacement-list(optional) (1) |
可以看到 Function-like macros 定义中最关键的点在于可变参数
C语言可变参数和宏定义的多参数
C语言本身 stdarg.h 头文件提供可变参数的能力,例如print函数是一个典型的应用
1 | double average(int num,...) |
宏本身也具备和C语言类似的可变参数能力,只需要在宏定义中包含关键词 VA_ARGS
1 |
即可表达可变参数的传递
操作符
在 Function-like macros 中除了关键词,还包含以下主要的操作符
操作符 | 符号 | 作用 |
---|---|---|
“Stringification” | # | 如果在 replacement-list 的某个 identifier添加操作符 |
“Concatenation” 、”Token Pasting” | ## |
Stringification 的作用
根据官方文档原文,字符串化操作符的主要作用是使用双引号把某个定义括起来,以字符串解析出来进行展开(Expand)
1 | a # operator before an identifier in the replacement-list runs the identifier through parameter replacement and encloses the result in quotes |
一个简单的举例, 其字符串化某个 identifier 时,如果identifier本身就是字符串,则不会有任何影响
1 | #define showtext(num) puts(#num) |
一个关键的点在于,如果和可变参数__VA_ARGS__共同使用时,会将整个可变参数变为一个字符串参数
1 | #define showlist(...) puts(#__VA_ARGS__) |
Concatenation 的作用
链接操作符的作用主要是将还未展开的先切断replacement-list,对前后的identifiers分别进行展开然后再拼接到一起,原文如下
1 | A ## operator between any two successive identifiers in the replacement-list runs parameter replacement on the two identifiers (which are not macro-expanded first) and then concatenates the result. |
这个操作符需要注意的地方是,操作符前后的定义必须是已知的identifiers,不然会报错,大多数中文博客将其理解成
“将宏定义中的多个参数连接形成一个参数”
1 | #define showtext(x,y) puts(x##y) |
非常规的操作符
还存在两种并不是所有编译器都支持的操作符 “#@” 和 “# #” 这里就不展开介绍了