6.10.5 宏替换
本节已细分为以下子页:
总则
约束
- 两个替换列表只有在预处理记号的个数、顺序、拼写和空白分隔都相同时,才称为相同;这里所有空白分隔都视为相同。
- 如果某个标识符当前被定义为对象式宏,那么除非第二个定义同样是对象式宏定义且两个替换列表相同,否则不得用另一条
#define重新定义它。 - 如果某个标识符当前被定义为函数式宏,那么除非第二个定义同样是函数式宏定义、具有相同个数和拼写的形参,且两个替换列表相同,否则不得用另一条
#define重新定义它。 - 在对象式宏定义中,标识符与替换列表之间必须有空白。
- 如果宏定义中的
identifier-list不以省略号结束,那么函数式宏调用中的实参数必须与形参数相等,这里也包括空实参。 - 如果宏定义中的
identifier-list以省略号结束,那么宏调用中的实参数至少要与命名形参数数量相同,不计入...。 - 宏调用中必须存在一个终止调用的
)预处理记号。 - 标识符
__VA_ARGS__和__VA_OPT__只能出现在使用省略号形参的函数式宏替换列表里。 - 函数式宏中的形参标识符在其作用域内必须唯一声明。
语义
define后紧跟的标识符称为宏名。宏名共用同一个名字空间。- 对于两种形式的宏,替换列表前后的空白字符都不视为替换列表的一部分。
- 如果某个
#预处理记号后面紧跟一个标识符,并且其词法位置恰好位于一条预处理指令可以开始的地方,那么该标识符不进行宏替换。 - 形如
c
#define identifier replacement-list1
的预处理指令定义一个对象式宏。此后该宏名的每次出现都会被替换为该指令其余部分构成的预处理记号替换列表;随后这个替换列表还会按本小节后文规则重新扫描。
- 形如
c
#define identifier(identifier-listopt) replacement-list
#define identifier(...) replacement-list
#define identifier(identifier-list, ...) replacement-list1
2
3
2
3
的预处理指令定义一个带形参的函数式宏。它的用法在语法上类似函数调用。
- 这些形参由可选的标识符列表给出;它们的作用域从标识符列表中的声明处开始,一直到终止该
#define指令的换行字符。 - 函数式宏名的后面如果紧跟一个
(作为下一个预处理记号,就会引入一段要被替换的预处理记号序列,也就是一次宏调用。 - 这个被替换的记号序列由与之匹配的
)终止;在寻找匹配)时,需要跳过内部配对的圆括号。 - 在函数式宏调用的预处理记号序列内部,换行被当作普通空白字符。
- 最外层匹配圆括号包围的预处理记号序列构成该函数式宏的实参列表。
- 实参列表中的各个实参由逗号分隔;但位于内部匹配圆括号之间的逗号不分隔实参。
- 如果实参列表中有某些预处理记号序列本来会被解释成预处理指令,那么行为未定义。
- 如果宏定义的标识符列表中出现了
...,那么尾随实参以及其间所有分隔逗号会并合成一个单一项,也就是可变实参。 - 并合后,实参数会比命名形参数多一个;但如果调用中实参数与命名形参数数量恰好相同,则其行为如同在实参列表末尾补了一个逗号,从而形成一个不含任何预处理记号的可变实参。