为什么这个表达式i + = i ++与Java和C不同?

时间:2014-05-08 22:01:53

标签: java c

我知道前缀和后缀操作...... ++ i和i ++之间的区别等等。

但我觉得我在这里遗漏了一些东西。您可以在下面找到代码:

package test;

public class Test
{
    public static void main (String[] args)
    {
        int i=0;

        i+=i++;
        System.out.println(i); // Prints 0

        i = i + (i++);
        System.out.println(i); // Prints 0

        i = i + (i+1);
        System.out.println(i); // Prints 1

    }
}

所以输出是:

0
0
1

我在C中尝试了相同的代码:

#include <stdio.h>
#include <string.h>

main()
{
    int i=0;

    i+=i++;

    printf("%d", i);   // prints 1

    i = i + (i++);
    printf("%d", i);   // prints 3

    i = i + (i+1);
    printf("%d", i);   // prints 7
}

,输出结果为:

1
3
7

为什么i+=i++不会增加i,而C中的相同代码会增加值?

8 个答案:

答案 0 :(得分:7)

<强>爪哇

在Java中,表达式具有明确定义的含义。 specification for the compound assignment operators说:

  

E1 op = E2 形式的复合赋值表达式等效于 E1 =(T)((E1)op(E2)),其中 T E1 的类型,但 E1 仅评估一次。

     

...

     

在运行时,表达式以两种方式之一进行评估。如果左侧操作数表达式不是数组访问表达式,则需要四个步骤:

     
      
  • 首先,评估左侧操作数以产生变量。如果此评估突然完成,则为赋值表达式   因同样的原因突然完成;右手操作数不是   评估并且没有分配。
  •   
  • 否则,保存左侧操作数的值,然后评估右侧操作数。如果此评估完成   然后突然完成赋值表达式   同样的原因,没有任何转让。
  •   
  • 否则,左侧变量的保存值和右侧操作数的值用于执行二进制操作   由复合赋值运算符表示。如果这个操作   突然完成,然后赋值表达式突然完成   出于同样的原因,没有任何转让。
  •   
  • 否则,二进制运算的结果将转换为左侧变量的类型,进行值集转换   (§5.1.13)到适当的标准值集(不是   扩展指数值集),转换结果是   存储在变量中。
  •   

所以

i += i++;

相当于

i = i + i++;

因为左侧的值在开始时保存,然后添加到右侧的值,并且因为Java中的表达式评估是从左到右,所以i的修改由i++引起的后续作业将覆盖该作业。

这一切都意味着

i += i++;

相当于

i += i;

<强> C

在C表达式

i += i++;

有未定义的行为。因此,执行此代码时可能会发生任何事情。这意味着您无法预测语句完成后的i值。

因此,完全可以预期C程序的输出与Java程序的输出不同。

答案 1 :(得分:6)

  

为什么这个表达式i + = i ++与Java和C不同?

i+=i++i = i + i++

相同

在Java(和C#)中,它是必需,这是从左到右计算的。那就是:

left = i;           // left of +
right = i;          // right of +, original value 
incr = right + 1;   // incremented value
i = incr;           // effect of ++
sum = left + right; // sum of left and right sides of +
i = sum;            // assign sum to i

在C中,可能但不是必需,这是从左到右计算的。特别是作业

left = i;

incr = right + 1;
i = incr;

可以按其他顺序完成。也就是说,这个排序在C:

中是 legal
right = i;          // right of +, original value 
incr = right + 1;   // incremented value
i = incr;           // effect of ++
left = i;           // left of +
sum = left + right; // sum of left and right sides of +
i = sum;

这显然会产生不同的结果。 (这不是唯一可能的重新排序,但它是合理的。)

所以,C和Java在这里是不同的,因为程序片段在C中没有很好地定义,以开头。在Java中,定义良好的,它只是糟糕的风格。

答案 2 :(得分:5)

i+=i++i = i + i++相同。如您的示例所示,i为零。

现在评估如下:

i = 0 + i++ //evaluate i++ to zero, then increment i by one

i = 0 + 0 //overwrite the value of i with 0 + 0

换句话说,你确实递增了它,但是立即用旧值覆盖它。

答案 3 :(得分:1)

这会分解为以下内部序列:

int temp1 = i;
int temp2 = i + i;
i = temp1 + 1;
i = temp2;

(请记住,i ++是后增量,而++ i是预增量。)

答案 4 :(得分:0)

您正在使用具有未定义行为的表达式

具体而言,同一表达式中同一操作数上的操作顺序没有明确定义。混合使用递减运算符时,最终会得到令人惊讶的结果,因为编译器可以自由重新排列它们发生的顺序。

答案 5 :(得分:0)

Java评估顺序为from left to rightoperands are evaluated before the operation。这意味着当i=0评估如下:

i+=i+1
is equivalent to:
i = i + (i++)

evaluating operators from left to right and knowing i=0
i = 0 + (i++)

evaluate i++. it returns 0 and assigns i=1
i = 0 + 0

evaluate 0+0=0
assign i=0, overwriting the previously assigned value of 1

Java代码中的第二个表达式与第一个表达式相同,但第三个表达式不同:

i = i + (i+1)

given that i=0, we evaluate left to right:

evaluate i as 0 and obtain:
i = 0 + (i+1)

evaluate i+1:
    evaluate i as 0 and obtain:
    0+1=1
thus we obtain:
i = 0 + 1
i = 1

注意(i + 1)没有副作用,它只返回i + 1的值,但不会改变i的值。

至于C代码,据我所知,这种情况下的执行顺序是未定义的,因此需要由编译器实现来决定。我可能错了这个,所以请纠正我。

答案 6 :(得分:0)

我将此视为一个Java问题,因为它确实有一个定义的结果。我不时会受到启发,根据Java语言规范分析其中一个问题,特别是Chapter 15 - Expressions

首先,i+=i++; i最初为0。

+=Compound Assignment Operators之一。对于int i,该语句相当于i = (int)(i+i++);

要评估+我们必须首先评估其左侧,值为0.接下来我们评估其右侧i++

++Postfix Increment Operator。关键语句是“后缀增量表达式的值是存储新值之前变量的值。”,即0。值1作为副作用存储到i ,但这没关系,因为在再次使用之前会有i的作业。

现在我们评估0 + 0。 +Additive Operators (+ and -) for Numeric Types之一。这是一个很好的简单的int添加,结果为0。

最后,我们通过对+=执行转换并完成Simple Assignment Operator =的转让来完成int,不需要转换,因为右侧类型为{{ 1}}。 int现在为0。

第二个声明是第一个展开i并且+=int次转化的声明。结果相同。

int的差异在于简单添加i = i + (i+1);的结果。再看一下Additive Operators (+ and -) for Numeric Types:“二进制+运算符在应用于两个数值类型的操作数时执行加法,产生操作数的总和。”该声明将+分配给0+(0+1)

将三个语句中的每一个视为等同于i,得到1,3,7,完全符合合理的C实现的可能性。

答案 7 :(得分:-1)

i ++将首先评估i,以便为什么在操作完成后得到0并且堆叠它将在之后递增的值。

所以如果你这样做

int i = 0;
System.out.println(i++); = 0
System.out.println(i++); = 1