根据C11标准,类函数宏的参数可以在其预处理标记序列的开头或结尾包含空格。对于正常更换,这种空间没有任何影响。对于字符串化,应忽略空格,如6.10.3.2.2
中所述第一个预处理标记之前和构成参数的最后一个预处理标记之后的空格被删除。
(我相信这也证明了参数可以在它周围有空格。)问题是,对于可以通过##运算符连接的参数,编译器应该对它的空格做什么?
我用VC ++尝试过,似乎只是忽略了空格。
我认为编译器应该用空格来执行连接。这可能会导致令牌无效,例如" ABC"由标识符ABC和带有空格作为第一个标记的参数形成。根据标准,如果以这种方式形成无效令牌,则行为未定义。
那么对于前面提到的VC ++所做的事情,它是否只是忽略空间或未定义行为的结果?
答案 0 :(得分:4)
我认为这也是证明参数可以在其周围留出空间的证据
不,实际上它只强化了标准的规定,即每个宏参数都是预处理令牌序列(C2011,6.10.3 / 11)。源文件中的空格分隔预处理标记;空白行不是自己预处理令牌。
您引用的标准部分可能会引起混淆,因为它混合了级别 - 源文件的字符序列,空白所属的字符序列,以及源序列的初始标记化产生的预处理标记序列。实际上,字符串化对于相邻令牌是否被源字符序列中的空格分隔很敏感,但任何此类空白的细节根本不重要 - 在字符串化时,相邻的白色空间分隔的标记由单个空格分隔结果字符串中的字符。
这并不意味着预处理令牌可以以空格开头或结尾。它不能;有关详细信息,请参阅标准的第6.4节。给定实现如何满足字符串化的规范必然是特定于实现的,但是实现可以这样做的一种方式将涉及为每个预处理标记维护布尔标志,该标记描述该标记是否在空白空间中在源序列中之前和/或之后。这些细节与解释标准为结果指定的内容无关,但是,对于字符串化运算符和标记粘贴运算符都没有。
对于将由##运算符连接的参数,编译器应该对其空格做什么?
当##
运算符(或#
运算符)发挥作用时,编译器已经完成了(直接)对源文件中出现的空格字符所做的一切,在将源标记化为预处理标记期间考虑它们。宏参数是预处理标记的序列,并且只有这些标记可以是字符串或字符文字或标题名称的范围内它们可能包含空格。此外,该标准规定:
如果在类函数宏的替换列表中,参数紧跟在
##
预处理标记之前或之后,则参数将被相应参数的 预处理标记序列替换 [...]
(C2011,6.10.3.3/2;重点补充)
再次,空格运行不是预处理令牌。宏扩展以及#
和##
运算符处理并处理预处理标记序列的级别。空格仅在令牌内部表示在此级别。来自源文件的非预处理标记内部的空格仅在预处理标记序列中间接且不确定地表示。
答案 1 :(得分:3)
6.10.3.3/3
在替换列表之前重新检查更多的宏名称来替换,##预处理令牌的每个实例 在替换列表中(不是从参数中)被删除并进行前面的预处理 token与以下预处理标记连接。
所以##
连接两个预处理标记。它们的参数是否包含空格无关紧要。