在C程序的翻译过程中,源代码被解析为preprocessor tokens个空格字符序列,并且每个注释都被更改为一个空格,符合C 2018 5.1.1.2.根据C 2018 6.10.3 10,宏替换在此序列预处理器标记和空白字符上操作,而不是在原始文本上操作.
问题中显示的宏的参数必须是预处理器令牌和空白字符的列表,不带空格逗号(因为逗号表示C 2018 6.10.3 11中的第二个参数,但宏定义时只有一个参数).
因此,开始时,MACRO(,)
和MACRO(a,b)
不是宏的定义调用.也不能在参数中包含单独的右括号,因为它会被视为参数列表的末尾.(您可以使用嵌套的左括号和右括号对,如MACRO(a(b,c)d)
中所示.请注意,这里的逗号由圆括号"保护",第一个)
被视为参数内开始(
的结束,而不是宏参数的结束.)
预处理器标记在C 2018 6.4 1中定义,它指的是C标准中其他地方定义的语法标记:identifier,pp-number,character-constant,string-literal,punctuator,以及每个不能是上述之一的非空白字符.(6.4还包括header-name,但根据C 2018 6.4 4,这仅用于#include
和#pragma
指令.
因此,要知道宏调用中定义了哪些字符序列,您需要考虑每个语法标记.
注意:该列表中的最后一个选项并不意味着允许任何字符.C实现具有一些源字符集,并且文件中可能存在未映射到源字符集中的任何字符的物理字符,编译器可能会拒绝此类字符.
除了这些被排除的字符之外,最后一个选项意味着很多东西将被接受为预处理器标记和空白的序列,因为任何不形成常规预处理标记的非排除字符序列都可以被视为"不能是上述之一的非空白字符".然而,具体说明什么是有效的是棘手的.由于预处理标记包括头名称、字符常量和字符串文字,因此上面提到的有问题的字符(逗号和括号)可能会相互作用,成为其中一个标记的一部分,而不是作为宏参数分隔符或终止符的独立字符.
在C 2018 6.10.3 11中,另一个行为显式未定义的序列是一个包含预处理指令的序列,如:
MACRO(
#define X Y
)
此外,在宏中使用#
运算符时,结果必须形成有效的字符串文字.例如,MACRO(\)
的形式为"\"
,这不是有效的字符串文字.同样对于#
,空白字符序列将被单个空格取代,因此MACRO(aXb)
,其中X
是空格、制表符和换行符的序列将仅变为a b
.