Why does this expression i+=i++ differs from Java and C?
我知道前缀和posfix操作…我和我之间的区别。
但我想我错过了一些东西。您可以在下面找到代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
所以输出是:
1 2 3 | 0 0 1 |
我在C中尝试了同样的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #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 2 3 | 1 3 7 |
为什么
爪哇
在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.
...
At run time, the expression is evaluated in one of two ways. If the left-hand operand expression is not an array access expression, then four steps are required:
- First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression
completes abruptly for the same reason; the right-hand operand is not
evaluated and no assignment occurs.- Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated. If this evaluation completes
abruptly, then the assignment expression completes abruptly for the
same reason and no assignment occurs.- Otherwise, the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation
indicated by the compound assignment operator. If this operation
completes abruptly, then the assignment expression completes abruptly
for the same reason and no assignment occurs.- Otherwise, the result of the binary operation is converted to the type of the left-hand variable, subjected to value set conversion
(§5.1.13) to the appropriate standard value set (not an
extended-exponent value set), and the result of the conversion is
stored into the variable.
所以
1 | i += i++; |
等于
1 | i = i + i++; |
因为左侧的值在开始时被保存,然后添加到右侧的值中,并且由于Java中的表达式求值是向右的,所以由EDCOX1 OR 2 6引起的EDCOX1×6的修改被随后的赋值覆盖。
所有这些都意味着
1 | i += i++; |
等于
1 | i += i; |
C
在c中,表达式
1 | i += i++; |
具有未定义的行为。所以当你执行这段代码的时候,任何事情都可能发生。这意味着您无法预测语句完成后
因此,C程序的输出与Java程序的输出不同,这是完全可以预料到的。
Why does this expression i+=i++ differs from Java and C?
在Java语言中(和C语言)需要从左到右进行计算。即:
1 2 3 4 5 6 | 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中,这是可能的,但不要求从左到右计算。特别是任务
1 | left = i; |
和
1 2 | incr = right + 1; i = incr; |
可以按其他顺序进行。也就是说,此命令在C中是合法的:
1 2 3 4 5 6 | 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中,它是定义良好的,只是坏的风格。
现在评估如下:
换言之,您确实会增加它,但会立即用旧值覆盖它。
这可分为以下内部顺序:
1 2 3 4 | int temp1 = i; int temp2 = i + i; i = temp1 + 1; i = temp2; |
(记住,i++是后增量,而+i是预增量。)
我把这个看作是Java问题,因为它确实有一个定义的结果。有时,我会从Java语言规范中,特别是第15章——表达式来分析这些问题中的一个。
首先,
要评估
现在我们计算0+0。
最后,我们通过对
第二个声明是第一个声明,扩展了
将这三条语句中的每一条都视为等同于
Java评估顺序是从左到右,在操作之前对操作数进行评估。这意味着当
1 2 3 4 5 6 7 8 9 10 11 12 | 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代码中的第二个表达式等价于第一个表达式,但第三个表达式不同:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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代码,据我所知,这种情况下的执行顺序是不确定的,因此由编译器实现来决定。我可能弄错了,所以请纠正我。
您使用的表达式具有未定义的行为。
具体来说,同一表达式中对同一操作数的操作顺序定义不明确。当混合使用递减运算符时,会得到令人惊讶的结果,因为编译器可以自由地重新排列它们发生的顺序。
i++将首先对i进行计算,以便在操作完成后获得0,并将其值叠加在一起。
所以如果你这样做