Почему это входит в бесконечный цикл? - PullRequest
490 голосов
/ 30 сентября 2010

У меня есть следующий код:

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 в качестве значения?

- обновление

Вот байт-код:

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

}

Я прочитаю инструкции , чтобы попытаться понять ...

Ответы [ 26 ]

7 голосов
/ 01 октября 2010

Вам не нужен машинный код, чтобы понять, что происходит.

В соответствии с определениями:

  1. Оператор присваивания вычисляет выражение правой части,и сохраняет его во временной переменной.

    1.1.Текущее значение x копируется во временную переменную

    1.2.Теперь значение x увеличивается.

  2. Затем временная переменная копируется в левую часть выражения, что случайно равно x!Вот почему старое значение x снова копируется в себя.

Это довольно просто.

5 голосов
/ 30 сентября 2010

Это потому, что в этом случае оно никогда не увеличивается. x++ будет использовать значение этого значения перед увеличением, как в этом случае это будет выглядеть так:

x = 0;

Но если вы сделаете ++x;, это увеличится.

3 голосов
/ 30 сентября 2010

Значение остается равным 0, поскольку значение x++ равно 0. В этом случае не имеет значения, увеличено или нет значение x, выполняется присвоение x=0.Это заменит временное увеличенное значение x (которое было 1 для «очень короткого времени»).

1 голос
/ 26 ноября 2011
    x++
=: (x = x + 1) - 1

Итак:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

Принимая во внимание

   ++x
=: x = x + 1

Итак:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

Конечно, конечный результат такой же, как просто x++; или ++x; на отдельной строке.

1 голос
/ 30 сентября 2010

Это работает так, как вы ожидаете от другого.Это разница между префиксом и постфиксом.

int x = 0; 
while (x < 3)    x = (++x);
1 голос
/ 30 сентября 2010

Думайте о х ++ как о вызове функции, которая «возвращает» то, что было до приращения (поэтому оно называется постинкрементным).

Итак, порядок работы:
1: кэшировать значение x перед увеличением
2: приращение х
3: вернуть кэшированное значение (x до его увеличения)
4: возвращаемое значение присваивается х

1 голос
/ 01 октября 2010

Когда ++ находится на правой стороне, результат возвращается до увеличения числа. Измените на ++ x, и это было бы хорошо. Java оптимизировал бы это для выполнения одной операции (присваивания x x), а не приращения.

1 голос
/ 02 октября 2010

Ну, насколько я вижу, ошибка возникает из-за присваивания, переопределяющего увеличенное значение на значение до увеличения, т. Е. Отменяет приращение.

В частности, выражение «x ++» имеет значение «x» до приращения, а не «++ x», которое имеет значение «x» после приращения.

Если вы заинтересованы в исследовании байт-кода, мы рассмотрим три строки:

 7:   iload_1
 8:   iinc    1, 1
11:  istore_1

7: iload_1 # Поместит значение 2-й локальной переменной в стек
8: iinc 1,1 # увеличит 2-ю локальную переменную на 1, обратите внимание, что стек остался нетронутым!
9: istore_1 # Извлечет вершину стека и сохранит значение этого элемента во 2-й локальной переменной
(Вы можете прочитать эффекты каждой инструкции JVM здесь )

Вот почему приведенный выше код будет зацикливаться бесконечно, в то время как версия с ++ x не будет. Байт-код для ++ x должен выглядеть совсем иначе, насколько я помню из компилятора Java 1.3, который я написал чуть больше года назад, байт-код должен выглядеть примерно так:

iinc 1,1
iload_1
istore_1

Так что, просто поменяв местами две первые строки, измените семантику так, чтобы значение, оставленное на вершине стека, после приращения (то есть «значение» выражения) было значением после приращения.

0 голосов
/ 10 декабря 2010

Оператор приращения применяется к той же переменной, к которой вы присваиваете. Это напрашивается на неприятности. Я уверен, что вы можете видеть значение вашей переменной x во время работы этой программы ... это должно прояснить, почему цикл никогда не заканчивается.

0 голосов
/ 30 сентября 2010

Интересно, есть ли что-нибудь в спецификации Java, которая точно определяет поведение этого. (Очевидно, это утверждение означает, что мне лень проверять.)

Обратите внимание, из байт-кода Тома, ключевые строки - 7, 8 и 11. Строка 7 загружает x в стек вычислений. Строка 8 увеличивает x. Строка 11 сохраняет значение из стека обратно в x. В обычных случаях, когда вы не присваиваете значения обратно самим себе, я не думаю, что была бы какая-либо причина, по которой вы не могли бы загрузить, сохранить, затем увеличить. Вы получите тот же результат.

Например, предположим, у вас был более нормальный случай, когда вы написали что-то вроде: г = (х ++) + (у ++);

Будь то сказано (псевдокод, чтобы пропустить технические детали)

load x
increment x
add y
increment y
store x+y to z

или

load x
add y
store x+y to z
increment x
increment y

не имеет значения. Я думаю, что любая реализация должна быть действительной.

Я бы очень осторожно относился к написанию кода, который зависит от этого поведения. Это выглядит очень зависящим от реализации, между трещинами в спецификациях для меня. Единственный раз, когда это будет иметь значение, если вы сделали что-то сумасшедшее, например, приведенный здесь пример, или если у вас было запущено два потока и вы зависели от порядка вычисления в выражении.

...