为什么复合赋值(+=)在不同语言(java,c++)之间有所不同?

ikfrs5lh  于 2021-07-12  发布在  Java
关注(0)|答案(2)|浏览(311)

定义 += 在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和java的文档,他们编写了类似的定义:
c

e1 op=e2(其中e1是一个可修改的左值表达式,e2是一个右值表达式或一个大括号的init list(因为c++11))与表达式e1=e1 op e2的行为完全相同,只是表达式e1只计算一次,并且对于不定序的函数调用,它的行为是一个单独的操作
java 语:
形式为e1 op=e2的复合赋值表达式等价于e1=(t)((e1)op(e2)),其中t是e1的类型,只是e1只求值一次。
出于好奇,我在python中检查了这个,它的输出与java相同。当然,编写这样的代码是非常糟糕的做法,但我仍然很想得到一个解释。
我怀疑变量求值方式的顺序对于 += 用不同的语言,但我不知道怎么说。我在定义中遗漏了什么,复合赋值运算符是如何计算的?

s71maibg

s71maibg1#

这与求值顺序的关系比“复合赋值运算符的作用”更大,因此您将在这两种语言规范的“求值顺序”部分中找到更多有用的内容。
对于java,jls§15.7:
二元运算符的左操作数似乎在右操作数的任何部分求值之前已完全求值。
如果运算符是复合赋值运算符(§15.26.2),则对左操作数的求值包括记住左操作数所表示的变量,以及获取并保存该变量的值以用于隐含的二进制运算。
所以呢 n 在…的左边 += 首先评估 0 . 然后右手边计算为 3 . 然后将该值与左侧的总和写入 n .
对于c++,求值顺序:
查看“规则”部分的第20项:
在每个简单赋值表达式e1=e2和每个复合赋值表达式e1@=e2中,e2的每次值计算和副作用都是在e1的每次值计算和副作用之前排序的
这里,首先计算e2(右手侧)到3,然后计算左手侧。那时, n 已由更改为3 f ,因此左侧也计算为3。

ccrfmcuu

ccrfmcuu2#

在java中,求值顺序是严格的左-右顺序。

n += f(3);

所以:'n'是0。f(3)返回3。所以我们加上0和3,把结果赋给n。f()中n的赋值无效。
java语言规范:
[…]保存左侧操作数的值,然后计算右侧操作数。[…]
对于c++,我认为(但没有检查)求值顺序是未定义的。
在你的例子中,f(3)被调用,n变成3,然后f(3)的结果被加到n的新值上。
要确定表达式的含义,不能只看所涉及的运算符。求值顺序很重要,在同一表达式中修改和使用变量时,精细打印很重要(结果可能用语言定义,也可能不用语言定义)。

相关问题