说,在下面的测试表达式中:
int ggg9 = fggg2() + (fggg3() && fggg4() < fggg5() * fggg6());
// 4 11 6 3
如果我们遵循operator precedence(在表达式下方的注释行中显示),则我认为将首先对parens中的表达式求值,然后将结果添加到fggg2()
。 / p>
因此,我假设它将按以下顺序解决:
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
编译器只是从左至右评估了所有这些功能,而与优先级无关。
答案 0 :(得分:5)
您正在将优先级与评估顺序混合在一起。
这是一个非常常见的混乱。可能是因为英语中的“ precede”一词具有时间含义。但这实际上是从排名层次结构(例如here)的意义上讲。
在更简单的表达式a() + b() * c()
中,优先级告诉我们*
运算符的操作数是b()
和c()
,而+
的操作数是a()
以及相乘的结果。一无所有,一无所有。
函数a,b,c
可能仍可以按任何顺序调用。
的确,*
的值计算必须在+
的值计算之前执行,因为我们需要知道前者的结果以便计算后者。
值计算是评估操作数的另一步骤。必须先计算运算符的操作数,然后再计算其值,但没有比这更严格的要求了。没有关于评估操作数的部分排序的规则。
+
的左操作数可能在+
的右操作数之前求值,即使右操作数由许多子表达式组成。编译器可能会评估所有“叶”操作数,将结果存储在堆栈中,然后执行值计算。
也不一定有严格的值计算顺序,例如在w() * x() + y() * z()
中,*
的两个值计算可能以任一顺序进行。
在您的代码中,&&
运算符的确有特殊的排序限制(有时称为短路)。在开始评估右操作数之前,必须先评估左操作数。
优先级告诉我们&&
的左操作数为fgg3()
,而&&
的右操作数为fggg4() < fggg5() * fggg6())
。因此,要求在fgg3()
,fgg4
和fgg5
中的任何一个之前调用fgg6
。在示例中,对操作数的求值顺序没有其他限制。 fgg2
可以随时出现,4
,5
,6
可以以任何顺序排列,只要它们都位于3
之后。 / p>
答案 1 :(得分:0)
您将运算符的优先级与求值顺序混淆了,虽然可以保证保留优先级,但不求值顺序,编译器可以自由选择顺序。
它可以首先在其他操作数的评估中间或之间评估fggg2()
。
答案 2 :(得分:0)
您忘记考虑&&
运算符的短路评估。
&&
运算符的左侧总是先于右侧进行求值。
为简化操作,必须对r3
的前半部分求值,如果它的值为false,则不会对您称为r1
和r2
的任何内容进行求值。
在C ++中需要进行短路评估。