[英]Why does compound assignment (+=) differ between languages (Java, C++)?
+=
的定义在 Java 和 C++ 中似乎是相同的,但是,它们的表现不同。
考虑 C++ 中的以下代码:
#include <iostream>
int n;
int f(int x) {
n += x;
return x;
}
int main() {
n = 0;
n = n + f(3);
std::cout<<n<<" ";
n = 0;
n += f(3);
std::cout<<n<<" ";
n = 0;
n = f(3) + n;
std::cout<<n<<std::endl;
}
这输出: 3 6 6
Java 中的类似代码输出: 3 3 6
,这里提供代码以供参考。
static int n;
public static void main(String[] args) {
n = 0;
n = n + f(3);
System.out.println(n);
n = 0;
n += f(3);
System.out.println(n);
n = 0;
n = f(3) + n;
System.out.println(n);
}
public static int f(int x) {
n += x;
return x;
}
C++:
E1 op= E2(其中 E1 是一个可修改的左值表达式,E2 是一个右值表达式或花括号初始化列表 (C++11 起))与表达式 E1 = E1 op E2 的行为完全相同,除了表达式 E1 仅被评估一次,并且对于不确定顺序的 function 调用,它表现为单个操作
Java:
E1 op= E2 形式的复合赋值表达式等同于 E1 = (T) ((E1) op (E2)),其中 T 是 E1 的类型,只是 E1 仅计算一次。
出于好奇,我在 Python 中查看了这个,它与 Java 具有相同的 output。当然,这样编写代码是非常糟糕的做法,但我仍然很好奇,希望得到解释。
我怀疑在不同的语言中, +=
的变量求值顺序是不同的,但我不知道具体是怎样的。 我在定义中遗漏了什么,复合赋值运算符是如何计算的?
这更多地与评估顺序有关,而不是“复合赋值运算符的作用”,因此您会在两种语言规范的“评估顺序”部分找到更多有用的东西。
对于 Java, JLS §15.7 :
二元运算符的左侧操作数似乎在右侧操作数的任何部分被求值之前被完全求值。
如果运算符是复合赋值运算符(第 15.26.2 节),则左侧操作数的计算包括记住左侧操作数表示的变量以及获取和保存该变量的值以用于隐含的二元运算.
因此+=
左侧的n
首先计算为0
。 然后右侧评估为3
。 然后将此值与左侧的总和写入n
。
对于 C++,评估订单:
查看“规则”部分中的第 20 项:
在每个简单赋值表达式 E1=E2 和每个复合赋值表达式 E1@=E2 中,E2 的每个值计算和副作用在 E1 的每个值计算和副作用之前排序
这里,首先评估 E2(右侧)到 3,然后评估左侧。 此时, n
已被f
更改为 3,因此左侧的计算结果也为 3。
评估顺序 - 严格按照 Java 中的左右顺序。
n += f(3);
所以:'n' 是 0。f(3) 返回 3。所以我们将 0 和 3 相加,并将结果赋给 n。 f() 中对 n 的赋值无效。
Java 语言规范:
[...] 保存左侧操作数的值,然后评估右侧操作数。 […]
对于 C++,我相信(但没有检查)评估顺序是未定义的。
在您的例子中,调用了 f(3),n 变为 3,然后将 f(3) 的结果添加到 n 的新值中。
要确定表达式的含义,不能只看涉及的运算符。 评估顺序很重要,并且在同一表达式中修改和使用变量的地方,细则很重要(结果可能会也可能不会在语言中定义)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.