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()
在 fgg4
、fgg5
和 fgg6
之前调用。在您的示例中,对操作数的评估顺序没有其他限制。 fgg2
可以随时出现,4
、5
、6
可以以任何顺序出现,只要它们都在 3
之后。 =38=]
您忘记考虑 &&
运算符的短路评估。
&&
运算符的左侧总是先于右侧求值。
在您试图简化操作时,必须评估 r3
的前半部分,如果它评估为 false,则 none 您所说的 r1
和 r2
将被评估。
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()
在 fgg4
、fgg5
和 fgg6
之前调用。在您的示例中,对操作数的评估顺序没有其他限制。 fgg2
可以随时出现,4
、5
、6
可以以任何顺序出现,只要它们都在 3
之后。 =38=]
您忘记考虑 &&
运算符的短路评估。
&&
运算符的左侧总是先于右侧求值。
在您试图简化操作时,必须评估 r3
的前半部分,如果它评估为 false,则 none 您所说的 r1
和 r2
将被评估。
C++ 中需要短路求值。