条件运算符是如何解析的?

How is the conditional operator parsed?

因此,cppreference 声称:

The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored.

但是,在我看来,':' 运算符之后的表达式部分也被解析为好像它在括号之间一样。我已经尝试用我的编程语言实现三元运算符(您可以看到解析表达式 here 的结果),并且我的解析器假装“:”之后的表达式部分也被括号括起来。例如,对于表达式 (1?1:0?2:0)-1,我的编程语言的解释器输出 0,这似乎与 C 兼容。例如,C 程序:

#include <stdio.h>

int main() {
    printf("%d\n",(1?1:0?2:0)-1);
}

输出 0.
如果我编写了我的编程语言的解析器,在解析三元运算符时,只需将第一个已经解析的节点放在':'之后并将其作为第三个操作数到'?:',它会输出与[=14相同的结果=],即1.
我的问题是这是否会(假装':'后面的表达式被括号括起来)总是与 C 兼容?

“假装是括号”是对运算符括号的某种描述。但当然,这必须相对于优先关系(包括结合性)来解释。所以在 a-b*ca*b-c 中,减法实际上就像它的参数被括起来一样,在 a-b-c 中只有左边的参数被这样处理,并且是比较运算符导致分组在 a<b-ca-b<c.

我相信你知道所有这些,因为你的解析器似乎适用于所有这些情况,但我这么说是因为三元运算符是右结合的,并且优先级低于任何其他运算符 [注 1]。这意味着由运算符优先级强加的伪括号围绕着右边的参数(不管它的主导运算符是什么,因为所有运算符都具有更高的优先级),以及左侧的参数,除非它的主导运算符是另一个条件运算符。但在 C 中情况并非如此,逗号运算符的优先级较低,不会被 :.

后面的假想括号括起来

了解复杂运算符的优先级的含义很重要。实际上,为了计算优先关系,我们首先将运算符折叠为一个简单的 ?:,其中包括封闭的(第二个)参数。这不是“好像表达式被括号括起来了”,因为它 括起来的。它被括在 ?: 之间,在这种情况下,它们在句法上是括号。

从这个意义上讲,它与通常将下标运算符作为后缀运算符进行分析非常相似,尽管下标运算符的括号中包含了第二个参数。下标运算符的优先级在逻辑上是将其视为单个 [] 的结果,抽象出其中包含的表达式。这也与函数调用运算符相同。这恰好是用括号写的,但精确的符号并不重要:可以想象一种替代语言,其中函数调用用不同的符号书写,可能是 {}。那根本不会影响语法。

?: 视为“括号”似乎很奇怪,因为它们 看起来 不是括号。但是解析器看不到符号的形状。它满足于被告知 () 关闭,在这种情况下,?: 关闭。 [注2]

说了这么多,我在条件表达式上试过你的编译器

d = 0 ? 0 : n / d

它正确地解析了这个表达式,但是编译后的代码在 验证d = 0 是否为真之前计算了n / d 。这不是条件运算符的工作方式;在这种情况下,它会导致意外的除以 0 异常。条件运算符必须首先计算其左侧参数,然后计算 恰好是其他两个表达式中的一个


备注:

  1. 在 C 中,这不太正确。逗号运算符的优先级较低,与赋值运算符的交互更复杂,它们在逻辑上具有相同的优先级并且也是右结合的。

  2. 在类 C 语言中,这些符号不用于任何其他目的,因此可以将它们视为看起来很奇怪的括号并保留它。但正如函数调用运算符(或者,就此而言,一元 - 运算符)的情况所示,有时可以出于多个目的重用运算符符号。

    出于好奇,开括号和闭括号不一定必须是不同的符号,只要它们不用于任何其他目的即可。因此,例如,如果 | 不用作运算符符号(就像在 C 中一样),那么您可以使用 | a | 表示 a 的绝对值而不会产生任何歧义.

    精确分析符号重用导致实际歧义的情况超出了本答案的范围。