Why does this go into an infinite loop?
Solution 1
Note: Originally I posted C# code in this answer for purposes of illustration, since C# allows you to pass int
parameters by reference with the ref
keyword. I've decided to update it with actual legal Java code using the first MutableInt
class I found on Google to sort of approximate what ref
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.
public MutableInt postIncrement(MutableInt x) {
int valueBeforeIncrement = x.intValue();
x.add(1);
return new MutableInt(valueBeforeIncrement);
}
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:
MutableInt x = new MutableInt();
x = postIncrement(x);
postIncrement(x)
does what? Increments x
, yes. And then returns what x
was before the increment. This return value then gets assigned to x
.
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:
MutableInt x = new MutableInt(); // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp; // Now x is 0 again.
Your fixation on the fact that when you replace x
on the left side of the above assignment with y
, "you can see that it first increments x, and later attributes it to y" strikes me as confused. It is not x
that is being assigned to y
; it is the value formerly assigned to x
. Really, injecting y
makes things no different from the scenario above; we've simply got:
MutableInt x = new MutableInt(); // x is 0.
MutableInt y = new MutableInt(); // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp; // y is still 0.
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 of x
to the console.
public class Main {
public static volatile int x = 0;
public static void main(String[] args) {
LoopingThread t = new LoopingThread();
System.out.println("Starting background thread...");
t.start();
while (true) {
x = x++;
}
}
}
class LoopingThread extends Thread {
public @Override void run() {
while (true) {
System.out.println(Main.x);
}
}
}
Below is an excerpt of the above program's output. Notice the irregular occurrence of both 1s and 0s.
Starting background thread... 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1
Solution 2
x = x++
works in the following way:
- First it evaluates expression
x++
. Evaluation of this expression produces an expression value (which is the value ofx
before increment) and incrementsx
. - Later it assigns the expression value to
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):
8: iload_1 // Remember current value of x in the stack 9: iinc 1, 1 // Increment x (doesn't change the stack) 12: istore_1 // Write remebered value from the stack to x
For comparison, x = ++x
:
8: iinc 1, 1 // Increment x 11: iload_1 // Push value of x onto stack 12: istore_1 // Pop value from the stack to x
Solution 3
This happens because the value of x
doesn't get incremented at all.
x = x++;
is equivalent to
int temp = x;
x++;
x = temp;
Explanation:
Let's look at the byte code for this operation. Consider a sample class:
class test {
public static void main(String[] args) {
int i=0;
i=i++;
}
}
Now running the class disassembler on this we get:
$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: return
}
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 the stack is popped out and stored in the local variable with index1
which isx
. -
iload_1
: The value at the location1
that is the value ofx
which is0
, is pushed into the stack. -
iinc 1, 1
: The value at the memory location1
is incremented by1
. Sox
now becomes1
. -
istore_1
: The value at the top of the stack is stored to the memory location1
. That is0
is assigned tox
overwriting its incremented value.
Hence the value of x
does not change resulting in the infinite loop.
Solution 4
- Prefix notation will increment the variable BEFORE the expression is evaluated.
- Postfix notation will increment AFTER the expression evaluation.
However "=
" has a lower operator precedence than "++
".
So x=x++;
should evaluate as follows
-
x
prepared for assignment (evaluated) -
x
incremented - Previous value of
x
assigned tox
.
Solution 5
None of the answers where quite spot on, so here goes:
When you're writing int x = x++
, you're not assigning x
to be itself at the new value, you're assigning x
to be the return value of the x++
expression. Which happens to be the original value of x
, as hinted in Colin Cochrane's answer .
For fun, test the following code:
public class Autoincrement {
public static void main(String[] args) {
int x = 0;
System.out.println(x++);
System.out.println(x);
}
}
The result will be
0
1
The return value of the expression is the initial value of x
, which is zero. But later on, when reading the value of x
, we receive the updated value , that is one.
The Student
Updated on July 26, 2020Comments
-
The Student almost 4 years
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++
orx=x+1
, but onx = x++
it should first attributex
to itself, and later increment it. Why doesx
continue with0
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...
-
p.campbell over 13 yearsDefinitely the correct implementation, but the question is 'why?'.
-
wkl over 13 yearsThe original code was using post-increment on x and then assigning it to x. x will be bound to x before increment, therefore it will never change values.
-
Ben Everard over 13 yearsWhy has this answer been down voted? Good answer @cletus... beat me to it :-)
-
Petar Minchev over 13 years@cletus I am not the downvoter, but your initial answer didn't contain the explanation. It just said do 'x++`.
-
p.campbell over 13 years@cletus: I didn't downvote, but your answer originally was just the
x++
code snippet. -
The Student over 13 yearsif you make a test, you can see that it first increments, and later attributes. So it should not attribute zero.
-
The Student over 13 yearsif you make a test, you can see that it first increments, and later attributes. So it should not attribute zero.
-
Sagar V over 13 yearsBut x++ is a post operation. So x would have to be incremented after the assignment is complete.
-
Progman over 13 years@Sagar V: only for the expression
x++
, not for the whole assignmentx=x++;
-
Rup over 13 yearsOK, but what specifies the order of steps 2 and 3?
-
Rup over 13 yearsOK, but what specifies the order of steps 3 and 4?
-
Rup over 13 yearsNo, I think it only needs to be incremented after the value of x to be used in the assignment was read.
-
Progman over 13 yearsActually it gets incremented (thats the meaning of
++
), but the variable gets overwritten later. -
Rup over 13 years@Tom that's the point, though - because this is all a single sequence it's doing things in a non-obvious (and probably undefined) order. By attempting to test this you're adding a sequence point and getting different behaviour.
-
The Student over 13 years"returns what X was before the increment" is wrong, see my update
-
jhabbott over 13 yearsIn reality steps 3 and 4 are not separate operations - it's not really a function call that returns a value, it just helps to think of it that way. Whenever you have an assignment the right hand side is "evaluated" then the result is assigned to the left hand side, the evaluation result can be thought of as a return value as it helps you to understand the order of operations, but it's not really.
-
Anon over 13 yearsRegarding your bytecode output: note that
iinc
increments a variable, it doesn't increment a stack value, nor does it leave a value on the stack (unlike almost every other arithmetic op). You might want to add the code generated by++x
for comparison. -
ILMTitan over 13 years@Rep It may not be defined in C or C++, but in Java, it is well defined.
-
Rup over 13 yearsOops, true. I meant steps 2 and 4 - why does the returned value get stored over the top of the incremented value?
-
Rup over 13 yearsIt's not the evaluation result that's the issue, though, it's the order of the stores.
-
Lordn__n over 13 yearsIt's called a "work in progress" and would've had to have been like 30 seconds in. Downvote for a partial answer 30 seconds old? Really?
-
jhabbott over 13 yearsThis is part of the definition of an assignment operation, first the right hand side is completely evaluated, then the result is assigned to the left hand side.
-
Colin Cochrane over 13 yearsI disagree. If x = 0 then x++ will return 0. Therefore x = x++ will result in x = 0.
-
Robert Munteanu over 13 years@Tom: see my answer - I show in a test that x++ actually returns the old value of x. That's where it breaks.
-
Jaydee over 13 years@jhabbot. Nearly right, technically x++ happens after expression evaluation (think of (x=a++ + b++)), but before the assignment as ++ has higher operator precedence.
-
Scott Chamberlain over 13 years
int temp = x; x = x + 1; x = temp;
its better not to use a tautology in your example. -
The Student over 13 yearsI'll try to understand the bytecode lines, see my update, so it'll be clear.. :)
-
Jaydee over 13 yearsIt is well defined in C ... but not terribly obvious.
-
Wooble over 13 yearsThe explanation is incorrect too. If the code first assigned x to x and then incremented x, it would work fine. Just change
x++;
in your solution tox=x; x++;
and you're doing what you claim the original code is doing. -
stevendesu over 13 years+1 for knowing the problem, but could have explained better: Where people seem to be confused- they think that "x" is a single entity. It's stored into itself, then incremented. The fact is, "x" is actually two variables in memory. There is "x" in RAM and "x" in the register. You're copying the value of "x" in RAM to "x" in the register, incrementing "x" in RAM, then copying the value of "x" in the register to "x" in RAM.
-
StriplingWarrior over 13 yearssteven: Your explanation gets a little closer, but even that is not entirely accurate, since optimization could cause the value of x to stay in a register the entire time. It doesn't necessarily have to go to RAM at all unless your method runs out of free registers.
-
rmeador over 13 yearsYou don't need to create a class to pass by reference in java (though that would certainly work). You can use the
Integer
class, which is part of the standard library, and it even has the benefit of being auto-boxed to and fromint
almost transparently. -
ILMTitan over 13 years@rmeador Integer is immutable, so you still couldn't change its value. AtomicInteger, however, is mutable.
-
Dan Tao over 13 years@rmeador: I'm asking because I honestly don't know, not to challenge you: how would this work, what you're suggesting? What I mean is, how can you assign a new value to
x
from within a method, even if it is boxed as anInteger
? (Is theInteger
type not immutable?) I know that in .NET, using C# for example, you could box anint
asobject
but you still wouldn't be able to assign a new value to the variable holding thatint
from within a method, unless it were passed as aref
parameter. -
rmeador over 13 years@Dan Tao: yeah, as @ILMTitan points out, it's immutable... I forgot that little detail. The suggestion of AtomicInteger seems reasonable, but I don't think you get autoboxing with that class.
-
RHSeeger over 13 years@Rup - The language defines it. The right side of the equation is evaluated first (in this case, "x++"), and the result is assigned to the variable on the left side. That's how the language works. As far as the "x++" "returning" x for the equation, that's how the postfix increment operator works (return the value of x, then increment it). If it had been "--x", then it would have been (increment x, then return the value). Return isn't the right word there, but you get the idea.
-
axtavt over 13 years@Dan: By the way,
x
in your last example must be declaredvolatile
, otherwise it's an undefined behaviour and seeing1
s is implementation specific. -
Dan Tao over 13 years@axtavt: Good point -- I actually discovered this myself after last updating my answer when I changed the code only slightly and saw different behavior (all 0s, no 1s). I've updated the answer to include the
volatile
keyword so that the program's behavior is defined. -
ErikE over 13 yearsUsing println() was very helpful to me in understanding this.
-
burkestar over 13 yearssimple answer: operator precedence of ++ before =. See cppreference.com/wiki/operator_precedence The "infinite loop" part of title is misleading.
-
Dan Tao over 13 years@burkestar: I don't think that link is quite appropriate in this case, since it's a Java question and (unless I'm mistaken) the behavior is actually undefined in C++.
-
Matthew Flaschen over 13 years+1. This is by far the best answer to the actual question, "Why?"
-
The Student over 13 yearsSomething to say about my last comment on the question?: It's very strange that in C language the behavior is different. Maybe in C it first read the value and then, later, it increments, while in Java it increments then return the old value..
-
The Student over 13 yearsSomething to say about my last comment on the question?: It's very strange that in C language the behavior is different. Maybe in C it first read the value and then, later, it increments, while in Java it increments then return the old value..
-
detly over 13 years@Tom Brito - in C it's not defined... the
++
could be done before or after assignment. Practically speaking, there might a compiler that does the same thing as Java, but you wouldn't want to bet on it. -
Matthew Flaschen over 13 yearsYour
x
/y
example is different from the real code, and the difference is relevant. Your link doesn't even mention Java. For two of the languages it does mention, the statement in the question is undefined. -
Matthew Flaschen over 13 yearsIt's not a question of precedence.
++
has higher precedence than=
in C and C++ too, but the statement is undefined. -
Justin Force over 13 yearsThis is the best answer. Some markup would have helped it stand out a bit more.
-
Matthew Flaschen over 13 yearsThis is wrong. It's not about precedence.
++
has higher precedence than=
in C and C++, but the statement is undefined in those languages. -
Michael Ekoka over 13 yearsRup is right about this. It's the order of the stores which is at issue in this particular case. y=x++ isn't the same as x=x++; On the latter one, x is being assigned 2 values in the same expression. Left hand x is being assigned the result of the evaluation of the expression x++, which is 0. Right hand side x is being incremented to 1. In which order these 2 assignment occur is what the issue is about. From previous posts it is clear that the way this works is: eval = x++ => eval == 0 : increment right x => x == 1 : left x = eval => x == 0
-
Justin Force over 13 yearsThe OP wasn't asking for a correct implementation, but for clarification on the expected behavior. He said that the code was a submission from his student, and that he knew that the syntax wasn't semantically sound.
-
detly over 13 years@Jaydee @Tom Brito - AFAIK it's undefined because there is no sequence point between assignment and increment.
-
Jaydee over 13 years@Detly you are correct I've had a chance to look at the language spec. I find it odd though.
-
detly over 13 years@Jaydee - it's only odd if you think the point of the standard is to specify the behaviour of every possible combination of syntactically valid constructs, but it's not. It's undefined because it was better to give compiler writers leeway on certain other things than to specify perverse constructs like this.
-
Jaydee over 13 years@Detly- I'd say the purpose of a standard is that a piece of code will operate the same way when compiled irrespective of the particular compiler used. I'd argue that the order of evaluation as occurs in the original question is the only correct one acording to the definitions given. Go figure, perhaps somebody (maybe me) should put a query into ISO/ANSI
-
detly over 13 years@Jaydee - almost... the purpose of the standard is that standard conforming code will operate the same way :) At any rate, there was (and maybe still is) an advantage to not specifying sequence points under every possible circumstance in C, but it's not really an advantage in Java.
-
Jaydee over 13 yearsInteresting article angelikalanger.com/Articles/VSJ/SequencePoints/…
-
Mark Peters over 13 yearsThis answer should probably be deleted; the solution doesn't answer the question and its explanation is patently wrong. Newbies that come here will only see that it has a fairly large positive score, not being able to know the huge amounts of downvotes.
-
Jaydee over 11 yearsThe original question is about Java
-
Charles Goodwin over 10 yearsWow, there's so much detail on this page when the answer is short and simple i.e. this one.
-
Jason C about 10 yearsThis is a very verbose way of saying the right side of an assignment expression is evaluated first.
-
Jim Balter over 7 years@burkestar Operator precedence is irrelevant here because
(x = x)++
isn't legal. And the "infinite loop" part of the title is a fact; it's not at all "misleading". -
Jim Balter over 7 years"if you make a test" -- some people seem to think that a test written in C tells us what Java will do, when it won't even tell us what C will do.