为什么这会进入无限循环?
我有以下代码:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
我们知道他应该只写 x++
或 x=x+1
,但在 x = x++
上它应该首先将 x
属性设置为自身,然后递增它。为什么 x
继续以 0
作为值?
--update
这是字节码:
public class Tests extends java.lang.Object{
public Tests();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_3
4: if_icmpge 22
7: iload_1
8: iinc 1, 1
11: istore_1
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
19: goto 2
22: return
}
我将阅读说明尝试去理解...
I have the following code:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
We know he should have writen just x++
or x=x+1
, but on x = x++
it should first attribute x
to itself, and later increment it. Why does x
continue with 0
as value?
--update
Here's the bytecode:
public class Tests extends java.lang.Object{
public Tests();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_3
4: if_icmpge 22
7: iload_1
8: iinc 1, 1
11: istore_1
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
19: goto 2
22: return
}
I'll read about the instructions to try to understand...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(26)
注意:最初我在此答案中发布 C# 代码是出于说明目的,因为 C# 允许您使用
ref
关键字通过引用传递int
参数。我决定使用第一个MutableInt
类,该类类似于ref
在 C# 中的作用。我真的无法判断这对答案是否有帮助或有害。我想说的是,我个人并没有做过那么多 Java 开发;据我所知,可能有更多惯用的方式来说明这一点。也许如果我们写出一个方法来执行与 x++ 相同的操作,这会让这一点变得更清楚。
正确的?递增传递的值并返回原始值:这就是后递增运算符的定义。
现在,让我们看看这种行为如何在示例代码中发挥作用:
postIncrement(x)
的作用是什么?递增x
,是的。然后返回增量之前的x
。然后,该返回值被分配给x
。因此,分配给
x
的值的顺序是 0,然后是 1,然后是 0。如果我们重写上面的内容,这可能会更清楚:
您对替换
x 的事实的关注
在上述 y 赋值的左侧,“你可以看到它首先递增 x,然后将其归因于 y”让我感到困惑。分配给y
的不是x
;而是它是之前分配给x
的值。确实,注入 y 使事情与上面的场景没有什么不同;我们简单地得到:所以很明显:
x = x++
实际上不会改变 x 的值。它总是导致 x 的值为 x0,然后是 x0 + 1,然后再次是 x0。更新:顺便说一句,为了避免您怀疑
x
在增量操作和上面示例中的赋值之间是否被赋值为 1,我整理了一个快速演示来说明这个中间值确实“存在”,尽管它永远不会在执行线程上“看到”。该演示在循环中调用
x = x++;
,同时单独的线程不断将x
的值打印到控制台。以下是上述程序输出的摘录。请注意 1 和 0 的不规则出现。
Note: Originally I posted C# code in this answer for purposes of illustration, since C# allows you to pass
int
parameters by reference with theref
keyword. I've decided to update it with actual legal Java code using the firstMutableInt
class I found on Google to sort of approximate whatref
does in C#. I can't really tell if that helps or hurts the answer. I will say that I personally haven't done all that much Java development; so for all I know there could be much more idiomatic ways to illustrate this point.Perhaps if we write out a method to do the equivalent of what
x++
does it will make this clearer.Right? Increment the value passed and return the original value: that's the definition of the postincrement operator.
Now, let's see how this behavior plays out in your example code:
postIncrement(x)
does what? Incrementsx
, yes. And then returns whatx
was before the increment. This return value then gets assigned tox
.So the order of values assigned to
x
is 0, then 1, then 0.This might be clearer still if we re-write the above:
Your fixation on the fact that when you replace
x
on the left side of the above assignment withy
, "you can see that it first increments x, and later attributes it to y" strikes me as confused. It is notx
that is being assigned toy
; it is the value formerly assigned tox
. Really, injectingy
makes things no different from the scenario above; we've simply got:So it's clear:
x = x++
effectively does not change the value of x. It always causes x to have the values x0, then x0 + 1, and then x0 again.Update: Incidentally, lest you doubt that
x
ever gets assigned to 1 "between" the increment operation and the assignment in the example above, I've thrown together a quick demo to illustrate that this intermediate value does indeed "exist," though it will never be "seen" on the executing thread.The demo calls
x = x++;
in a loop while a separate thread continuously prints the value ofx
to the console.Below is an excerpt of the above program's output. Notice the irregular occurrence of both 1s and 0s.
x = x++
按以下方式工作:x++
。对该表达式求值会产生一个表达式值(即递增之前的x
值)并递增x
。因此,事件的顺序如下所示(这是一个实际的反编译字节码,由
javap -c
生成,带有我的注释):为了进行比较,
x = ++x
:x = x++
works in the following way:x++
. Evaluation of this expression produces an expression value (which is the value ofx
before increment) and incrementsx
.x
, overwriting incremented value.So, the sequence of events looks like follows (it's an actual decompiled bytecode, as produced by
javap -c
, with my comments):For comparison,
x = ++x
:发生这种情况是因为
x
的值根本没有增加。相当于
解释:
让我们看一下这个操作的字节码。考虑一个示例类:
现在运行类反汇编程序,我们得到:
现在 Java VM 是基于堆栈,这意味着对于每个操作,数据将被压入堆栈,并且从堆栈中弹出数据以执行操作。还有另一种数据结构,通常是存储局部变量的数组。局部变量被赋予 id,它们只是数组的索引。
让我们看看
main()
方法中的助记符:0
被推入堆栈。
istore_1
:顶部元素堆栈被弹出并存储在
索引为
1
的局部变量
是
x
。iload_1
: 的值位置
1
,即x
的值其
0
被压入堆栈。iinc 1, 1
: 处的值内存位置
1
递增1
。所以x
现在变成<代码>1。
istore_1
:顶部的值堆栈存储到内存位置
1
。即0
被赋值x
覆盖其增量值。因此,
x
的值不会改变,从而导致无限循环。This happens because the value of
x
doesn't get incremented at all.is equivalent to
Explanation:
Let's look at the byte code for this operation. Consider a sample class:
Now running the class disassembler on this we get:
Now the Java VM is stack based which means for each operation, the data will be pushed onto the stack and from the stack, the data will pop out to perform the operation. There is also another data structure, typically an array to store the local variables. The local variables are given ids which are just the indexes to the array.
Let us look at the mnemonics in
main()
method:iconst_0
: The constant value0
is pushed on to the stack.
istore_1
: The top element of thestack is popped out and stored in the
local variable with index
1
which
is
x
.iload_1
: The value at thelocation
1
that is the value ofx
which is
0
, is pushed into the stack.iinc 1, 1
: The value at thememory location
1
is incremented by1
. Sox
now becomes1
.istore_1
: The value at the top ofthe stack is stored to the memory location
1
. That is0
is assignedto
x
overwriting its incremented value.Hence the value of
x
does not change resulting in the infinite loop.但是,“
=
”的运算符优先级低于“++
”。因此,
x=x++;
应按如下方式求值x
准备赋值(已求值)x
递增x
的先前值分配给x
。However "
=
" has a lower operator precedence than "++
".So
x=x++;
should evaluate as followsx
prepared for assignment (evaluated)x
incrementedx
assigned tox
.没有一个答案是完全正确的,所以这里是:
当您编写
int x = x++
时,您并没有将x
分配为新值本身,您将x
指定为x++
表达式的返回值。这恰好是x
的原始值,如 科林·科克伦的回答 。为了好玩,测试以下代码:
结果将是
表达式的返回值是
x
的初始值,即零。但稍后,当读取x
的值时,我们收到更新后的值,即 1。None of the answers where quite spot on, so here goes:
When you're writing
int x = x++
, you're not assigningx
to be itself at the new value, you're assigningx
to be the return value of thex++
expression. Which happens to be the original value ofx
, as hinted in Colin Cochrane's answer .For fun, test the following code:
The result will be
The return value of the expression is the initial value of
x
, which is zero. But later on, when reading the value ofx
, we receive the updated value , that is one.其他人已经解释得很好了。我只包含相关 Java 规范部分的链接。
x = x++ 是一个表达式。 Java 将遵循评估顺序。
它将首先计算表达式 x++,该表达式 将递增x 并将结果值设置为 x 的先前值。
然后它将分配表达式结果到变量 x。最后,x 恢复到之前的值。
It has been already explained well by other. I just include the links to the relevant Java specification sections.
x = x++ is an expression. Java will follow the evaluation order.
It will first evaluate the expression x++, which will increment x and set result value to the previous value of x.
Then it will assign the expression result to the variable x. At the end, x is back at its previous value.
该语句:
计算结果如下:
x
压入堆栈;x
;x
。所以数值不变。将其与: 计算
结果为:
x
;x
压入堆栈;x
。你想要的是:
This statement:
evaluates like this:
x
onto the stack;x
;x
from the stack.So the value is unchanged. Compare that to:
which evaluates as:
x
;x
onto the stack;x
from the stack.What you want is:
答案非常简单。它与评估事物的顺序有关。
x++
返回值x
,然后递增x
。因此,表达式
x++
的值为0
。因此,您每次都在循环中分配x=0
。当然,x++
会递增该值,但这发生在赋值之前。The answer is pretty straightforward. It has to do with the order things are evaluated.
x++
returns the valuex
then incrementsx
.Consequently, the value of the expression
x++
is0
. So you are assigningx=0
each time in the loop. Certainlyx++
increments this value, but that happens before the assignment.来自 http://download.oracle.com/javase/tutorial/java/ nutsandbolts/op1.html
为了说明这一点,请尝试以下操作:
它将打印 1 和 0。
From http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
To illustrate, try the following:
Which will print 1 and 0.
您实际上并不需要机器代码来了解发生了什么。
根据定义:
赋值运算符计算右侧表达式,并将其存储在临时变量中。
1.1。 x 的当前值被复制到这个临时变量中
1.2。 x 现在递增。
然后临时变量被复制到表达式的左侧,恰好是 x!这就是为什么 x 的旧值再次被复制到自身中。
这很简单。
You don't really need the machine code to understand what's happending.
According the definitions:
The assignment operator evaluates the right-hand side expression, and stores it in a temporary variable.
1.1. The current value of x is copied into this temporary variable
1.2. x is incremented now.
The temporary variable is then copied into the left-hand side of the expression, which is x by chance! So that's why the old value of x is again copied into itself.
It is pretty simple.
您实际上得到了以下行为。
想法是,后递增运算符(x ++)在返回其值以在其所使用的方程中使用之后递增该变量。
编辑:由于注释而添加了一点点。考虑如下。
You're effectively getting the following behavior.
The idea being that the post-increment operator (x++) increments that variable in question AFTER returning its value for use in the equation it's used in.
Edit: Adding a slight bit because of the comment. Consider it like the following.
这是因为在这种情况下它永远不会增加。
x++
将首先使用它的值,然后再增加,就像在本例中一样:但是如果你这样做
++x;
这将会增加。This is because it never gets incremented in this case.
x++
will use the value of it first before incrementing like on this case it will be like:But if you do
++x;
this will increase.该值保持为 0,因为
x++
的值为 0。在这种情况下,无论x
的值是否增加,赋值x=0
被执行。这将覆盖x
的临时递增值(“非常短的时间”为 1)。The value stays at 0 because the value of
x++
is 0. In this case it doesn't matter if the value ofx
is increased or not, the assignmentx=0
is executed. This will overwrite the temporary incremented value ofx
(which was 1 for a "very short time").这将按照您期望的方式进行。这就是前缀和后缀的区别。
This works how you expect the other one to. It's the difference between prefix and postfix.
将 x++ 视为一个函数调用,它“返回”X 在增量之前的内容(这就是为什么它被称为后增量)。
所以操作顺序是:
1:在递增之前缓存x的值
2:增加x
3:返回缓存的值(x在增加之前)
4:返回值赋给x
Think of x++ as a function call that "returns" what X was before the increment (that's why it's called a post-increment).
So the operation order is:
1: cache the value of x before incrementing
2: increment x
3: return the cached value (x before it was incremented)
4: return value is assigned to x
当 ++ 位于右侧时,在数字递增之前返回结果。
改成++x就可以了。
Java 会对其进行优化以执行单个操作(将 x 分配给 x)而不是增量。
When the ++ is on the rhs, the result is returned before the number is incremented.
Change to ++x and it would have been fine.
Java would have optimised this to perform a single operation (the assignment of x to x) rather than the increment.
据我所知,发生错误是因为赋值覆盖了增量值,而增量之前的值,即它撤消了增量。
具体来说,“x++”表达式在递增之前具有“x”值,而“++x”在递增之后具有“x”值。
如果您有兴趣研究字节码,我们将看一下相关的三行:
7: iload_1 # 将把第二个局部变量的值放入堆栈
8: iinc 1,1 # 会将第二个局部变量加 1,注意它不会影响堆栈!
9: istore_1 # 将弹出堆栈顶部并将该元素的值保存到第二个局部变量
阅读每个 JVM 指令的效果)
(您可以在此处 这就是为什么上面的代码会无限循环,而带有 ++x 的版本则不会。
++x 的字节码应该看起来完全不同,据我记得一年多前我编写的 1.3 Java 编译器,字节码应该是这样的:
所以只需交换前两行,改变语义,这样增量后留在堆栈顶部的值(即表达式的“值”)是增量后的值。
Well as far as I can see, the error occurs, due to the assignment overriding the incremented value, with the value prior to incrementation, i.e. it undoes the increment.
Specifically, the "x++" expression, has the value of 'x' prior to increment as opposed to "++x" which has the value of 'x' after incrementation.
If you are interested in investigating the bytecode, we will take a look at the three lines in question:
7: iload_1 # Will put the value of the 2nd local variable on the stack
8: iinc 1,1 # will increment the 2nd local variable with 1, note that it leaves the stack untouched!
9: istore_1 # Will pop the top of stack and save the value of this element to the 2nd local variable
(You can read the effects of each JVM instruction here)
This is why the above code will loop indefinitely, whereas the version with ++x will not.
The bytecode for ++x should look quite different, as far as I remember from the 1.3 Java compiler I wrote a little over a year ago, the bytecode should go something like this:
So just swapping the two first lines, changes the semantics so that the value left on the top of stack, after the increment (i.e. the 'value' of the expression) is the value after the increment.
所以:
而
所以:
当然,最终结果与单独一行的
x++;
或++x;
相同。So:
Whereas
So:
Of course the end result is the same as just
x++;
or++x;
on a line by itself.检查下面的代码,
输出将是,
后增量
表示增量值并返回增量之前的值。这就是temp
值为0
的原因。那么如果temp = i
并且这是在循环中(第一行代码除外)怎么办?就像问题中一样!!!!Check the below code,
the output will be,
post increment
means increment the value and return the value before the increment. That is why the valuetemp
is0
. So what iftemp = i
and this is in a loop (except for the first line of code). just like in the question !!!!因为上面的陈述 x 永远不会达到 3;
because of above statement x never reaches 3;
我想知道 Java 规范中是否有任何内容精确定义了此行为。 (该语句的明显含义是我懒得检查。)
请注意 Tom 的字节码,关键行是 7、8 和 11。第 7 行将 x 加载到计算堆栈中。第 8 行增加 x。第 11 行将堆栈中的值存储回 x。在正常情况下,如果您没有将值赋回给它们自己,我认为没有任何理由不能加载、存储然后递增。你会得到相同的结果。
就像,假设你有一个更正常的情况,你写了类似的内容:
z=(x++)+(y++);
无论它说(跳过技术细节的伪代码)
还是
应该无关紧要。我认为,这两种实施方式都应该是有效的。
对于编写依赖于这种行为的代码,我会非常谨慎。对我来说,它看起来非常依赖于实现,处于规范的裂缝之间。唯一会产生影响的情况是,如果您做了一些疯狂的事情,例如这里的示例,或者您有两个线程正在运行并且依赖于表达式中的求值顺序。
I wonder if there's anything in the Java spec that precisely defines the behavior of this. (The obviously implication of that statement being that I'm too lazy to check.)
Note from Tom's bytecode, the key lines are 7, 8 and 11. Line 7 loads x into the computation stack. Line 8 increments x. Line 11 stores the value from the stack back to x. In normal cases where you are not assigning values back to themselves, I don't think there would be any reason why you couldn't load, store, then increment. You would get the same result.
Like, suppose you had a more normal case where you wrote something like:
z=(x++)+(y++);
Whether it said (pseudocode to skip technicalities)
or
should be irrelevant. Either implementation should be valid, I would think.
I'd be extremely cautious about writing code that depends on this behavior. It looks very implementation-dependent, between-the-cracks-in-the-specs to me. The only time it would make a difference is if you did something crazy, like the example here, or if you had two threads running and were dependent on the order of evaluation within the expression.
我认为因为在Java ++中比=(赋值)具有更高的优先级......是吗?
看看 http://www.cs.uwf.edu/~eelsheik /cop2253/resources/op_precedence.html...
同样,如果你写 x=x+1...+ 的优先级高于 = (赋值)
I think because in Java ++ has a higher precedence than = (assignment)...Does it?
Look at http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html...
The same way if you write x=x+1...+ has a higher precedence than = (assignment)
x++
表达式的计算结果为x
。++
部分影响评估之后的值,而不是语句之后的值。所以x = x++
被有效地转换为The
x++
expression evaluates tox
. The++
part affect the value after the evaluation, not after the statement. sox = x++
is effectively translated into在将值加一之前,先将该值赋给变量。
Before incrementing the value by one, the value is assigned to the variable.
发生这种情况是因为它是后期递增的。这意味着在计算表达式后变量会递增。
x 现在是 10,但 y 是 9,即 x 增加之前的值。
更多信息请参阅后增量的定义。
It's happening because it's post incremented. It means that the variable is incremented after the expression is evaluated.
x is now 10, but y is 9, the value of x before it was incremented.
See more in Definition of Post Increment.
增量运算符应用于您要分配的同一变量。那是自找麻烦。我确信您可以在运行该程序时看到 x 变量的值......这应该清楚为什么循环永远不会结束。
The increment operator is applied to the same variable as you are assigning to. That's asking for trouble. I am sure that you can see the value of your x variable while running this program.... that's should make it clear why the loop never ends.