C/C++ 编译器如何根据运算符的优先级和关联性分离标记?

Posted

技术标签:

【中文标题】C/C++ 编译器如何根据运算符的优先级和关联性分离标记?【英文标题】:C/C++ How does compiler separate tokens according to operator's precedence and associativity? 【发布时间】:2013-11-10 02:39:31 【问题描述】:

考虑以下代码:

int a = 3;
int b = 0;
b = a > 0 ? ++b, ++a : --a, b = 0;

执行后,我得到b的值变成0a的值变成4。 这意味着条件表达式的结果,a > 0 被评估为true 并且表达式a++ 已执行,而, 之后的表达式b = 0 也已执行。换句话说,表达式b = 0 不是三元运算符的操作数,而++b 是。否则,b = 0 将不会被执行,因为条件表达式不会被评估为 false

我的问题是“编译器根据什么规则将b = 0踢出三元运算符的操作数?”

第三条语句中的运算符包括:++--,优先级最高,>,优先级第二,? :=,优先级第三和, 优先级最低。我知道优先级较高的运算符应该更早地确定它们的操作数,以便首先处理++-->。那么语句等效为:

b = (a > 0) ? (++b), (++a) : (--a), b = 0;

现在,轮到 =?: 处理了。 =?: 的关联性是right-to-left,所以我认为编译器会从右端解析语句。遇到的第一个运算符是=,所以b = 0 被组合在一起。第二个遇到的运算符是,。由于它的优先级低于当前正在分析的运算符,我假设编译器会跳过它。然后编译器遇到了:,它是三元运算符的一部分,所以一直解析。(其实我不知道编译器在解析整个三元运算符之前怎么知道:?:的一部分) 问题来了。 编译器遇到的下一个运算符是,,但编译器还没有确定?: 的操作数。 , 的优先级低于 ?:。理论上应该跳过;令人惊讶的是,在实际测试中,(++b)(++a) 此时已经被, 运算符连接起来,并且都被认为是?: 的操作数。这让我很困惑。 为什么最后一个, 被忽略,不包含在?: 的操作数中,而前面的, in 语句保留在三元运算符的操作数中?

有人可以用这个例子澄清优先级和关联性的概念吗?第一次看到这段代码时,我对执行结果感到非常困惑。我原以为表达式b=0 也是三元运算符操作数的一部分;因此b = 0 只会在a > 0false 时执行。

提前致谢。

【问题讨论】:

int b; 然后++b 看起来像未定义的行为。 是的,我已经更正了b 的声明。本来b是全局作用域的变量,所以初始化为0。谢谢提醒。 【参考方案1】:

优先级和关联性是不同的概念,但从技术上讲,C 和 C++ 标准没有指定任何概念。相反,他们给出了语法规则来推断表达式的结构。

相关规则为:

conditional-expression:
    logical-or-expression
    logical-or-expression ? expression : assignment-expression

expression:
    assignment-expression
    expression , assignment-expression

primary-expression:
    ( expression )

postfix-expression:
    primary-expression
    ...

等等……

这个想法是每种类型的表达式都可以生成一个复合表达式或另一种较低优先级的表达式。您只能使用括号向上到根expression

考虑到这一点,请注意使用?:conditional-expression 实际上在三个子表达式中的每一个中都有不同类型的表达式。中间的是expression,所以它可以接受任何类型的表达,即使是,=(这里没有歧义,因为结尾是:)。

但请注意,最后一个是assignment-expression,这是除, 之外的任何一种表达式。如果你想使用它,你必须用() 将它括起来,创建一个primary-expression

额外说明:第一个表达式是logical-or-expression,如果您仔细查看语法,您会发现它不包括赋值运算符、条件运算符和逗号运算符。

所以你的表情:

b = a > 0 ? ++b, ++a : --a, b = 0

实际上是一个expression逗号assignment-expression,其中第一个expressionb = a > 0 ? ++b, ++a : --a,第二个assignment-expressionb = 0

等等……

【讨论】:

【参考方案2】:

你的表达式被评估为(b = ((a > 0) ? (++b, ++a) : (--a))), (b = 0);

正如您所说,?: 的优先级高于逗号运算符,因此 b=0 不属于三元条件。三元运算符左右部分的区别在于,在左侧,编译器尝试将完整的字符串 ++b, ++a 评估为表达式(知道 ?: 之间的部分必须是表达式,而在右侧,编译器会尽可能地解析表达式。运算符的优先级表示编译器必须在,处停止。在左侧,编译器不会在,处停止,因为这是表达式的合法部分。

【讨论】:

以上是关于C/C++ 编译器如何根据运算符的优先级和关联性分离标记?的主要内容,如果未能解决你的问题,请参考以下文章

具有两次以上递归的运算符优先级

C# 运算符的优先级和关联性

逻辑运算的优先级

读高质量C++/C编程指南第4章

Java 表达式顺序、运算符优先级和关联性之间的区别

C/C++ 三元运算符实际上是不是具有与赋值运算符相同的优先级?