C 通过令牌连接重复调用宏是否未指定行为?

C 通过令牌连接重复调用宏是否未指定行为?,c,macros,c-preprocessor,c11,C,Macros,C Preprocessor,C11,C11标准允许在宏扩展中出现的至少一种情况存在模糊性,即类似宏的函数扩展到其未经验证的名称,并由下一个预处理令牌调用。本标准中给出的示例如下 #define f(a) a*g #define g(a) f(a) // may produce either 2*f(9) or 2*9*g f(2)(9) 该示例没有说明当宏M展开时会发生什么,并且结果的全部或部分通过令牌连接到第二个预处理令牌M(被调用)时会发生什么 问题:这样的调用被阻止了吗 下面是这样一个调用的示例。只有在使用一组相当复杂的

C11标准允许在宏扩展中出现的至少一种情况存在模糊性,即类似宏的函数扩展到其未经验证的名称,并由下一个预处理令牌调用。本标准中给出的示例如下

#define f(a) a*g
#define g(a) f(a)

// may produce either 2*f(9) or 2*9*g
f(2)(9)
该示例没有说明当宏M展开时会发生什么,并且结果的全部或部分通过令牌连接到第二个预处理令牌M(被调用)时会发生什么

问题:这样的调用被阻止了吗

下面是这样一个调用的示例。只有在使用一组相当复杂的宏时,才会出现这个问题,因此本例是为了简单起见而设计的

// arity gives the arity of its args as a decimal integer (good up to 4 args)
#define arity(...) arity_help(__VA_ARGS__,4,3,2,1,)
#define arity_help(_1,_2,_3,_4,_5,...) _5

// define 'test' to mimic 'arity' by calling it twice
#define test(...) test_help_A( arity(__VA_ARGS__) )
#define test_help_A(k) test_help_B(k)
#define test_help_B(k) test_help_##k
#define test_help_1 arity(1)
#define test_help_2 arity(1,2)
#define test_help_3 arity(1,2,3)
#define test_help_4 arity(1,2,3,4)

// does this expand to '1' or 'arity(1)'?
test(X)
test(X)
扩展为
test\u help\u A(arity(X))
,它在重新扫描时调用
test\u help\u A
,在替换之前扩展其arg,因此与
test\u help\u A(1)
相同,后者生成
test\u help\u B(1)
,后者生成
test\u help\u 1
。这一点很清楚

所以,问题来了
test\u help\u 1
是使用一个字符
1
生成的,该字符来自
arity
的扩展。那么
test\u的扩展是否可以帮助\u 1
再次调用arity?我的gcc和clang版本都这么认为

有人能说gcc和clang所做的解释是标准中的要求吗?
有人知道一个实现对这种情况有不同的解释吗?我认为gcc和clang的解释是正确的。
arity
的两个扩展不在同一个调用路径中。第一个是扩展
test\u help\u A
的参数,第二个是扩展
test\u help\u A
本身


这些规则的思想是保证不存在无限递归,这在这里是有保证的。在两次调用之间对宏的评估有了进展。

您所说的“阻塞”是什么意思?在第一个示例中,即标准中的一个示例中,实现可以“阻塞”第二次扩展
f
,以
2*f(9)
结束,而不是
2*9*g
。F的调用是可以考虑无效的,因为它可以被认为是递归的。换句话说,
f
的调用被阻止。我想问的是,同样的推理是否可以证明阻止最后一次扩展
arity
的实现是合理的。顺便说一句,下次你问这样一个相当技术性的问题时,你能把你的例子减少到最低限度吗?在这里,
arity
的多个参数和不同版本的
test\u help\u X
只是分散了注意力。我想我应该使用较小的最大arity,但是删除
test\u help\u B
会产生
test\u help\u arity(\u VA\u ARGS\uuuuu)
,不是吗?我不确定这个参数是否适用,正如标准所述,如果调用从宏替换中获取名称,并从替换外部获取括号内的arg列表,则调用是否嵌套是未指定的。可以说,这样的拆分嵌套也不会产生递归的可能性,但这一点尚未明确。我的示例只是将拆分嵌套的思想扩展到由扩展边框分隔的宏名称,而不是将宏名称与其arg列表分隔开来。这两种情况似乎太相关了,无法做出任何其他地方不支持的假设。尽管如此,谢谢你的回答,这有助于我更轻松地假设这种情况不会阻碍宏观扩张。如果他们这么做了,那就太反常了。我想我将继续这个假设。