C++ 中对运算符优先级的混淆

Confusion over operator precedence in C++

比如说,在下面的测试表达式中:

int ggg9 = fggg2() + (fggg3() && fggg4() < fggg5() * fggg6());
//                 4          11         6         3

如果我们遵循 operator precedence(显示在表达式下方带有注释的行中),我会假设首先计算括号中的表达式,然后将结果添加到 fggg2() .

因此我假设它将按以下顺序解决:

int r1 = fggg5() * fggg6(); //Precedence 3 (inside parens)
int r2 = fggg4() < r1;      //Precedence 6 (inside parens)
int r3 = fggg3() && r2;     //Precedence 11 (inside parens)
int ggg9 = fggg2() + r3;    //Outside of parens, so it's last

但是 x86-64 gcc 8.2 resolves it 这样的:

(我会把它转换回 C++)

    int i0;
    int r2 = fggg2();
    int r3 = fggg3();
    if(!r3) goto L13;
    int r4 = fggg4();
    int r5 = fggg5();
    int r6 = fggg6();
    int i1 = r5 * r6;
    if(r4 >= i1) goto L13
    i0 = 1;
    goto L14
L13:
    i0 = 0;
L14:
    int ggg9 = i0 + r2;

那么,当 && 运算符的优先级分别为 11、3、6 时,为什么 && 运算符似乎在 * 之前被求值,然后在 < 之前被求值?

最后,为什么先评估 fggg2() 似乎并不关心 parens? VS2017好像是最后评估的

据我所知,gcc 编译器只是简单地从左到右评估所有这些函数,而不考虑优先级。

您将求值顺序与运算符的优先级混淆了,虽然保证保留优先级,但求值顺序不是,编译器可以自由选择顺序。

它可能首先评估fggg2(),在评估其他操作数的中间或之间。

您混淆了优先级求值顺序

这是一个很常见的混淆。也许是因为英语中的"precede"这个词可以有时间的含义。但实际上它是指等级等级意义上的(例如 here)。

在更简单的表达式a() + b() * c()中,优先级告诉我们*运算符的操作数是b()c()+的操作数是a()和乘法的结果。仅此而已。

函数 a,b,c 可能仍会以任何顺序调用。

*值计算确实必须在+值计算之前执行], 因为我们需要知道前者的结果才能计算后者。

值计算是计算操作数的不同步骤。运算符的操作数必须在其值计算之前进行评估,但没有比这更严格的要求。没有关于评估操作数的偏序的规则。

+ 的左操作数可能在 + 的右操作数之前计算,即使右操作数由许多子表达式组成。编译器可能会评估所有 "leaf" 操作数,将结果存储在堆栈中,然后执行值计算。

价值计算也不一定有严格的顺序,例如在 w() * x() + y() * z() 中,* 的两个值计算可以按任意顺序进行。


在您的代码中,&& 运算符确实有一个特殊的排序限制(有时称为 短路)。必须在开始计算右操作数之前计算左操作数。

优先级告诉我们&&的左操作数是fgg3()&&的右操作数是fggg4() < fggg5() * fggg6()).因此,要求 fgg3()fgg4fgg5fgg6 之前调用。在您的示例中,对操作数的评估顺序没有其他限制。 fgg2 可以随时出现,456 可以以任何顺序出现,只要它们都在 3 之后。 =38=]

您忘记考虑 && 运算符的短路评估。

&& 运算符的左侧总是先于右侧求值。

在您试图简化操作时,必须评估 r3 的前半部分,如果它评估为 false,则 none 您所说的 r1r2 将被评估。

C++ 中需要短路求值。