在类似对象的宏中是否允许#
,如果允许,会发生什么?
C 标准仅定义宏中#
的行为,用于类似函数的宏。
示例代码:
#include <stdio.h>
#define A X#Y
#define B(X) #X
#define C(X) B(X)
int main()
{
printf(C(A) "\n");
}
gcc输出X#Y
,表示它允许#
出现并且不执行任何特殊处理。但是,由于#
运算符的定义在这种情况下没有定义行为,它实际上是未定义的行为吗?
答案 0 :(得分:3)
正如您所注意到的,#
仅在类似函数的宏中具有已定义的效果。 §6.10.3.2/1(所有对标准的引用都是针对C11草案(N1570))。要查看类对象宏中发生的情况,我们必须查看其他地方。
表单
的预处理指令# define identifier replacement-list new-line
定义了一个 对象般的宏 这会导致宏名称的每个后续实例 由构成的预处理令牌的替换列表替换 指令的其余部分。 [...]
§6.10.3/ 9
因此,唯一的问题是#
中是否允许replacement-list
。如果是这样,它会照常替换。
我们在§6.10/ 1 中找到语法:
replacement-list:
pp-tokens (opt.)
pp-tokens:
preprocessing-token
pp-tokens preprocessing-token
现在,#
是有效的preprocessing-token
吗? §6.4/ 1 说:
preprocessing-token:
header-name
identifier
pp-number
character-constant
string-literal
punctuator
each non-white-space character that cannot be one of the above
肯定不是header-name
(§6.4.7/ 1 ),identifier
令牌中不允许这样做(§6.4.2.1/1),也不是pp-number
(基本上是允许格式中的任何数字,§6.4.8/ 1 ),也不是character-constant
(例如{ {1}},§6.4.4.4/1)或u'c'
(正是您所期望的,例如string-literal
,§6.4.5/ 1 )。
但是,它在§6.4.6/ 1 中列为L"String"
。因此,允许在类似对象的宏的punctuator
中进行逐字复制。它现在需要按照§6.10.3.4中的描述进行重新扫描。让我们看一下你的例子:
replacement-list
将替换为C(A)
。 C(X#Y)
在这里没有特殊效果,因为它不在#
replacement-list
中,而在于C
。 C(X#Y)
显然已变为B(X#Y)
。然后,通过B
的{{1}}中的#
运算符将B
的参数转换为字符串文字,产生replacement-list
因此,您没有未定义的行为。