预处理器指令中的逗号运算符

Comma-Operator in Preprocessor-Directives

这是一个加深理解的问题。

假设我有以下代码:

#define DEF 1,2,3
#if (DEF > 3)
#endif

如果我是对的,则 if 应该始终为真,因为 1 和 2 是没有影响的表达式,只会检查 3。 (这至少在正常 C/C++ 代码中应该是正确的)

但这是对的吗?我是否也应该考虑 1 和 2?我在一些代码中看到了它,但我不确定这是否有任何意义或影响。

#define DEF 1,2,3
#if (DEF > 3)
#endif

上面的if条件会失败。原因 3 > 3 是错误的。 也许你可以把它写成

#define DEF 1,2,3
#if (DEF == 3)
#endif

DEF 将扩展为 #if (1, 2, 3 > 3)。因为 , 的优先级较低,所以 "does" 任何东西的唯一表达式是 3 > 3,这当然是错误的。

是的,你是对的,因为前两个是虚拟表达式,只有第三个表达式会被实际计算。

考虑代码:

#define DEF 1,2,3
#if (DEF > 3)
#endif

将被视为:

#if (1,2,3 > 3)
#endif

, 的优先级低于 >,因此 3 > 3 结果为 0。然后,#if (1,2,0) 肯定会评估为 #if (0),它始终是 false 值。

您的示例代码不严格符合。

来自 C11,6.10.1 标准杆。 3(条件包含,语义):

Preprocessing directives of the forms

# if constant-expression new-line groupopt

# elif constant-expression new-line groupopt

check whether the controlling constant expression evaluates to nonzero

然而6.6标准杆。 3(常量表达式,约束)说:

Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.115)

脚注 115 是:

The operand of a sizeof or _Alignof operator is usually not evaluated (6.5.3.4).

C++11 标准允许逗号运算符,但更改只是无意的。 Clang 和 GCC 等实现会评估 #if (1,0),但它们会在 -pedantic 下发出警告,尽管这绝对是轻微的后果,但这是它们对非法预处理器构造的典型响应。他们对宏重新定义做同样的事情,这是完全有害的。

warning: comma operator in operand of #if [-Wpedantic]

"feature" 计划(DR 1436)从标准中删除,但对于如何执行此操作或如何处理预处理器标准的维护存在持续的不确定性一般。

(需要说明的是,在C++11之前的C语言和C++中,常量表达式中明确禁止使用逗号。见cremno的回答。无意中的更改是删除了通用语言的"constant expressions shall not contain…" ,但没有为预处理器添加回规则。)

我已经提议 (N4220) 对预处理器规范进行全面更新,但它在没有正式审查的情况下在一个小组委员会中停滞不前。尽管如此,几个感兴趣的委员会成员(来自 Clang、GCC 和 IBM XLC)确实审查了它,并且普遍认为该文件中的调整反映了标准的真实意图。从那篇论文的§4.4,

Note that the comma operator is not allowed even within parentheses.

实际上,不,逗号运算符既不便携也不适合未来。几个人已经考虑过这个问题并同意你不应该在那里使用它。但是,按照表达式求值的规则,12 只是被忽略是对的。