我知道前缀和后缀操作...... ++ 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中的相同代码会增加值?
答案 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:
中是 legalright = 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 right和operands 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