运算符计算方向

7

我遇到了一些我无法理解的东西。

我有这段代码:

cout << "f1 * f1 + f2 * f1 - f1 / f2 is: "<< f1 * f1 + f2 * f1 - f1 / f2 << endl;

所有的"f"都是对象,而且所有运算符都被重载了。

奇怪的是第一次计算使用了/运算符,然后是第二个*,接着是第一个*;之后是+运算符,最后是-运算符。

所以基本上,/*从右到左运行,而+-运算符则从左到右运行。

我进行了另一个测试…… 我检查了这段代码:

 cout << "f1 * f1 / f2 is: " << f1 * f1 / f2 << endl;

现在,第一个运算符是*,然后才是/运算符。现在,它从左到右工作。
有人能帮我理解为什么方向会有区别吗?
十分感谢!

在编程中,使用反引号而不是双引号来表示运算符。否则,markdown解析器会混淆。 - Marcelo Cantos
也许只是因为在第一个例子中,*/ 的顺序无关紧要,所以它以某种“优化”的方式执行了它?! - Draco Ater
7个回答

10
这又是一个关于函数参数求值顺序的问题 - C++没有规定这样的顺序。你的代码等价于:
(f1 * f1) + (f2 * f1) - (f1 / f2)

三个乘法和除法操作可以以任何顺序进行评估。对于具有名称的函数来说,这可能更清晰:

add(f1*f2,f2*f1)).minus(f1/f2);

底线是,结合性和优先级对函数参数和/或子表达式的求值顺序没有任何影响。给定简单的表达式:
a + b

无论是否重载'+',C++(和C)编译器都可以自由地先计算a,然后是b,或者先计算b,然后是a。


是的!尼尔,你显然处于前20%的水平;)值得注意的是,仅因为有很多错误答案,关联性并不受意外的评估顺序影响。 - Potatoswatter
1
或者,sub( add( mul( f1, f2 ), mul( f2, f1 ) ), div( f1, f2 ) ) - Potatoswatter

4

运算符参数的计算顺序不确定。

C++ 标准 5/4:

除非另有说明,否则单个运算符和单个表达式的子表达式的操作数的计算顺序以及副作用发生的顺序是未指定的。

你的表达式等价于以下表达式(*/ 也是运算符,但保留原样):

operator-( operator+(f1*f1, f2*f1), f1/f2 )

0

运算符优先级定义了具有不同优先级的运算符的顺序,例如,*/总是在+-之前计算。然后,当多个具有相同优先级的运算符连接在一起时,遵循从左到右的规则。

然而,除了逻辑和三元运算符之外,没有关于应该首先评估运算符参数的规则。编译器可以自由地以任何顺序执行乘法操作,然后将它们传递给加法运算符。

实际上,在表达式f() + g() + h()中,编译器可以自由地按相反的顺序调用函数,h(),然后是g(),然后是f(),然后将f()和g()的结果相加,最后将h()的结果相加。在大多数情况下,这不是一个非常明智的做法,但它是完全合法的。


0

用户定义的运算符使用与内置运算符相同的优先级和结合规则。

优先级规则指出,当表达式中相邻的运算符(由单个运算符分隔)具有不同的优先级时,应先执行具有较高优先级的运算符,然后再执行具有较低优先级的运算符。

结合规则指出,在表达式中包含具有相同优先级的相邻运算符时,应按照哪种顺序执行这些运算符。

  • 在您的第一个示例中,优先级规则适用,但由于结合性仅涉及相邻运算符,编译器会选择以何种顺序执行乘法和除法。

  • 在您的第二个示例中,应用了结合规则。

避免出现此类规则问题(可能有些复杂)的经验法则:

  • 如果不确定,请使用括号或本地变量来强制顺序。
  • 调用函数(或用户定义的运算符)时避免副作用,因为结果可能令人惊讶。
  • 重新定义运算符时,请尽量与数学一致。

-1

我没有点踩,但最可能的原因是计算顺序已经定义,实际操作不会重新排序。还要考虑到存在真正的差异:int a = 5, b = 2, c = 10; assert( 1 == (a*b/c) ); assert( 0 == (a/c*b) ) 对于双精度浮点数来说,可以找到一个更复杂的例子,因为计算机中的操作实际上是离散的,所以计算顺序确实会修改最终结果。 - David Rodríguez - dribeas
@dribeas - David Rodríguez - 我从你的示例中没有理解如何排序会影响结果。 - Andrey
abc 都是 int 类型时,若 a/c = 0,则 a/c*b = 0 - Bill
事实是,在离散数学中,操作的顺序很关键。在简单的整数示例中,a/c 的结果是0,但 a*b/c 的结果是1。因此,即使在真实世界的数学中,乘法和除法的顺序不重要 (a*b/c = a/c*b),在计算机中得到的结果是不同的。 - David Rodríguez - dribeas

-1

这只是简单的运算符优先级问题。

在您的第一个示例中,所有乘法和除法必须在加法和减法之前完成。由于乘法和除法的结果是独立的,因此它们执行的顺序并不重要,只要在加法和减法中从左到右使用结果即可。

在第二个示例中,乘法和除法不是独立的,必须从左到右执行。

根据运算符优先级,您始终会得到正确的结果。这才是真正重要的。编译器不能保证除了遵守运算符优先级外还有其他的评估顺序。


-1

尼尔是正确的。 这是运算符结合性和优先级的问题。 根据规则,表达式被评估为(f1 * f1)+(f2 * f1)-(f1 / f2),然后在第二次通过中从左到右进行。最后进行加法和减法。

第二个例子很简单。*和/具有相同的优先级,因此我们按照结合性规则评估表达式,即从左到右的顺序。


4
尼尔并没有说你似乎认为他说的那样。 - anon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接