为什么复合赋值(+=)在不同编程语言(如Java、C++)之间有所不同?

huangapple go评论118阅读模式
英文:

Why does compound assignment (+=) differ between languages (Java, C++)?

问题

以下是翻译好的部分:

C++代码输出:3 6 6
Java代码输出:3 3 6

观察C++文档Java文档,它们提供了类似的定义:

C++:
>E1 op= E2(其中E1是可修改的左值表达式,E2是右值表达式或花括号初始化列表(自C++11起))与表达式E1 = E1 op E2的行为完全相同,只是表达式E1仅被评估一次,并且在不确定顺序的函数调用方面它表现为单个操作

Java:
>形式为E1 op= E2的复合赋值表达式等效于E1 =(T)((E1)op(E2)),其中T是E1的类型,除了E1仅被评估一次。

出于好奇,我在Python中进行了验证,它的输出与Java相同。当然,编写这样的代码是极其不良的实践,但我仍然对其解释很感兴趣。

我怀疑在不同语言中,+= 的变量评估顺序不同,但我不知道具体是怎样的。我在定义中可能忽略了什么内容,复合赋值运算符是如何评估的呢?

英文:

The definitions of += seem to be the same in both Java and C++, however, they perform differently.

Consider the following code in 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;
}

This outputs: 3 6 6

Similar code in Java outputs: 3 3 6, here is the code for reference.

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;
}

Looking at the documentation for C++ and Java, they write similar definitions:

C++:
>E1 op= E2 (where E1 is a modifiable lvalue expression and E2 is an rvalue expression or a braced-init-list (since C++11)) is exactly the same as the behavior of the expression E1 = E1 op E2, except that the expression E1 is evaluated only once and that it behaves as a single operation with respect to indeterminately-sequenced function calls

Java:
>A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

Out of curiosity, I checked this in Python, and it has the same output as Java. Of course, writing code like this is extremely bad practice, but I'm still curious for an explanation.

I suspect that the ordering of the way variables get evaluated is different for += in different languages, but I don't know exactly how. What in the definitions am I missing, and how are compound assignment operators evaluated?

答案1

得分: 3

这与“复合赋值运算符的操作”相比,更多涉及到“评估顺序”,因此您会在两种语言的规范的“评估顺序”部分找到更多有用的信息。

对于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。

英文:

This has more to do with evaluation order than "what the compound assignment operators do", so you'll find more useful things in the "evaluation order" sections of the specs of both languages.

For Java, JLS §15.7:

> The left-hand operand of a binary operator appears to be fully
> evaluated before any part of the right-hand operand is evaluated.
>
> If the operator is a compound-assignment operator (§15.26.2), then
> evaluation of the left-hand operand includes both remembering the
> variable that the left-hand operand denotes and fetching and saving
> that variable's value for use in the implied binary operation.

So n on the left of += evaluates first, to 0. Then the right hand side evaluates to 3. The sum of this value and the left hand side is then written to n.

For C++, Evaluation Order:

Look at item 20 in the "Rules" section:

> In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1

Here, E2 (the right hand side) is evaluated first, to 3, then the left hand side is evaluated. At that point, n is already changed to 3 by f, so the left hand side evaluates to 3 as well.

答案2

得分: 2

评估顺序 - 在Java中严格从左到右。

n += f(3);

所以:'n' 为 0。 f(3) 返回 3。 因此,我们将 0 和 3 相加,并将结果赋值给 n。 函数 f() 内部对 n 的赋值没有影响。

Java 语言规范:

[...] 保存左操作数的值,然后评估右操作数。 [...]

对于 C++,我相信(但没有检查过)评估顺序是未定义的。

在您的情况下,f(3) 被调用,n 变为 3,然后 f(3) 的结果被添加到 n 的新值中。


要确定表达式的含义,不能仅看涉及的运算符。评估顺序很重要,当一个变量在同一个表达式中被修改和使用时,细节很重要(在语言中,结果可能甚至未被定义)。

英文:

Evaluation order - strictly left-right in Java.

n += f(3);

So: 'n' is 0. f(3) returns 3. So we add 0 and 3, and assign the result to n. The assignment to n inside f() has no effect.

Java language spec:

> […] the value of the left-hand operand is saved and then the
> right-hand operand is evaluated. […]

For C++, I believe (but did not check) that evaluation order is undefined.

In your case, f(3) was called, n became 3, and then the result of f(3) was added to the new value of n.


To determine the meaning of an expression, you can't just look at the operators involved. Evaluation order matters, and where a variable is modified and used in the same expression, the fine print matters (the result may or may not even be defined in the language).

huangapple
  • 本文由 发表于 2020年8月20日 11:05:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/63497705.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定