Why does this go into an infinite loop?

24,101

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 of x before increment) and increments x.
  • 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 value 0 is pushed on to the stack.
  • istore_1: The top element of the stack is popped out and stored in the local variable with index 1
    which is x.
  • iload_1 : The value at the location 1 that is the value of x which is 0, is pushed into the stack.
  • iinc 1, 1 : The value at the memory location 1 is incremented by 1. So x now becomes 1.
  • istore_1 : The value at the top of the stack is stored to the memory location1. That is 0 is assigned to x overwriting its incremented value.

Hence the value of x does not change resulting in the infinite loop.

Solution 4

  1. Prefix notation will increment the variable BEFORE the expression is evaluated.
  2. Postfix notation will increment AFTER the expression evaluation.

However "=" has a lower operator precedence than "++".

So x=x++; should evaluate as follows

  1. x prepared for assignment (evaluated)
  2. x incremented
  3. Previous value of x assigned to x.

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.

Share:
24,101
The Student
Author by

The Student

Updated on July 26, 2020

Comments

  • The Student
    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++ 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...

  • p.campbell
    p.campbell over 13 years
    Definitely the correct implementation, but the question is 'why?'.
  • wkl
    wkl over 13 years
    The 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
    Ben Everard over 13 years
    Why has this answer been down voted? Good answer @cletus... beat me to it :-)
  • Petar Minchev
    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
    p.campbell over 13 years
    @cletus: I didn't downvote, but your answer originally was just the x++ code snippet.
  • The Student
    The Student over 13 years
    if you make a test, you can see that it first increments, and later attributes. So it should not attribute zero.
  • The Student
    The Student over 13 years
    if you make a test, you can see that it first increments, and later attributes. So it should not attribute zero.
  • Sagar V
    Sagar V over 13 years
    But x++ is a post operation. So x would have to be incremented after the assignment is complete.
  • Progman
    Progman over 13 years
    @Sagar V: only for the expression x++, not for the whole assignment x=x++;
  • Rup
    Rup over 13 years
    OK, but what specifies the order of steps 2 and 3?
  • Rup
    Rup over 13 years
    OK, but what specifies the order of steps 3 and 4?
  • Rup
    Rup over 13 years
    No, I think it only needs to be incremented after the value of x to be used in the assignment was read.
  • Progman
    Progman over 13 years
    Actually it gets incremented (thats the meaning of ++), but the variable gets overwritten later.
  • Rup
    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
    The Student over 13 years
    "returns what X was before the increment" is wrong, see my update
  • jhabbott
    jhabbott over 13 years
    In 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
    Anon over 13 years
    Regarding 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
    ILMTitan over 13 years
    @Rep It may not be defined in C or C++, but in Java, it is well defined.
  • Rup
    Rup over 13 years
    Oops, true. I meant steps 2 and 4 - why does the returned value get stored over the top of the incremented value?
  • Rup
    Rup over 13 years
    It's not the evaluation result that's the issue, though, it's the order of the stores.
  • Lordn__n
    Lordn__n over 13 years
    It'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
    jhabbott over 13 years
    This 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
    Colin Cochrane over 13 years
    I disagree. If x = 0 then x++ will return 0. Therefore x = x++ will result in x = 0.
  • Robert Munteanu
    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
    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
    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
    The Student over 13 years
    I'll try to understand the bytecode lines, see my update, so it'll be clear.. :)
  • Jaydee
    Jaydee over 13 years
    It is well defined in C ... but not terribly obvious.
  • Wooble
    Wooble over 13 years
    The 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 to x=x; x++; and you're doing what you claim the original code is doing.
  • stevendesu
    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
    StriplingWarrior over 13 years
    steven: 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
    rmeador over 13 years
    You 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 from int almost transparently.
  • ILMTitan
    ILMTitan over 13 years
    @rmeador Integer is immutable, so you still couldn't change its value. AtomicInteger, however, is mutable.
  • Dan Tao
    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 an Integer? (Is the Integer type not immutable?) I know that in .NET, using C# for example, you could box an int as object but you still wouldn't be able to assign a new value to the variable holding that int from within a method, unless it were passed as a ref parameter.
  • rmeador
    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
    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
    axtavt over 13 years
    @Dan: By the way, x in your last example must be declared volatile, otherwise it's an undefined behaviour and seeing 1s is implementation specific.
  • Dan Tao
    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
    ErikE over 13 years
    Using println() was very helpful to me in understanding this.
  • burkestar
    burkestar over 13 years
    simple answer: operator precedence of ++ before =. See cppreference.com/wiki/operator_precedence The "infinite loop" part of title is misleading.
  • Dan Tao
    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
    Matthew Flaschen over 13 years
    +1. This is by far the best answer to the actual question, "Why?"
  • The Student
    The Student over 13 years
    Something 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
    The Student over 13 years
    Something 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
    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
    Matthew Flaschen over 13 years
    Your 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
    Matthew Flaschen over 13 years
    It's not a question of precedence. ++ has higher precedence than = in C and C++ too, but the statement is undefined.
  • Justin Force
    Justin Force over 13 years
    This is the best answer. Some markup would have helped it stand out a bit more.
  • Matthew Flaschen
    Matthew Flaschen over 13 years
    This 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
    Michael Ekoka over 13 years
    Rup 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
    Justin Force over 13 years
    The 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
    detly over 13 years
    @Jaydee @Tom Brito - AFAIK it's undefined because there is no sequence point between assignment and increment.
  • Jaydee
    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
    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
    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
    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
    Jaydee over 13 years
  • Mark Peters
    Mark Peters over 13 years
    This 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
    Jaydee over 11 years
    The original question is about Java
  • Charles Goodwin
    Charles Goodwin over 10 years
    Wow, there's so much detail on this page when the answer is short and simple i.e. this one.
  • Jason C
    Jason C about 10 years
    This is a very verbose way of saying the right side of an assignment expression is evaluated first.
  • Jim Balter
    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
    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.