У меня есть следующий код:
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
}
Я прочитаю инструкции, чтобы попытаться понять ...
x++
- постинкрементное ;x=
присвоение результата ; результат отx++
оригинальногоx
(и есть побочный эффект прироста, но это не меняет результата), так что это может быть истолковано какvar tmp = x; x++; x = tmp;
Ответы:
Примечание : Первоначально я написал код C # в этом ответе в целях иллюстрации, поскольку C # позволяет передавать
int
параметры по ссылке сref
ключевым словом. Я решил обновить его с помощью действующего легального Java-кода, используя первыйMutableInt
класс, который я нашел в Google, чтобы приблизиться к тому, чтоref
делает в C #. Я не могу действительно сказать, помогает ли это или ранит ответ. Я скажу, что лично я не столько занимался разработкой Java; поэтому, насколько я знаю, может быть гораздо больше идиоматических способов проиллюстрировать этот момент.Возможно, если мы напишем метод, чтобы сделать эквивалент того, что
x++
он делает, это станет понятнее.Правильно? Увеличьте переданное значение и верните исходное значение: это определение оператора постинкремента.
Теперь давайте посмотрим, как это проявляется в вашем примере кода:
postIncrement(x)
делает то, что? Приращенияx
да. И затем возвращает то, чтоx
было до приращения . Это возвращаемое значение затем присваиваетсяx
.Таким образом, порядок значений присваивается
x
0, затем 1, затем 0.Это может быть еще яснее, если мы переписаем выше:
Ваша зацикленность на том факте, что когда вы заменяете
x
в левой части вышеприведенного задания наy
«вы можете видеть, что сначала он увеличивает x, а позже приписывает его y», меня смущает. Это неx
то, что присваиваетсяy
; это значение, ранее присвоенноеx
. Действительно, инъекцияy
делает вещи ничем не отличающимися от сценария выше; мы просто получили:Так что понятно:
x = x++
эффективно не меняет значение х. Это всегда приводит к тому, что x принимает значения x 0 , затем x 0 + 1, а затем снова x 0 .Обновление : Между прочим, чтобы вы не сомневались, что
x
когда-либо будет присвоено 1 "между" операцией приращения и назначением в примере выше, я собрал короткую демонстрацию, чтобы проиллюстрировать, что это промежуточное значение действительно "существует", хотя оно и будет никогда не будет "замечен" в исполняющем потоке.Демонстрация вызывает
x = x++;
в цикле, в то время как отдельный поток непрерывно выводит значениеx
на консоль.Ниже приведена выдержка из вышеприведенной программы. Обратите внимание на нерегулярное появление 1 и 0.
источник
Integer
класс, который является частью стандартной библиотеки, и он даже имеет преимущество в том, что он автоматически упакован в и изint
почти прозрачно.x
в вашем последнем примере должно быть объявленоvolatile
, в противном случае это неопределенное поведение, а просмотр1
s зависит от реализации.++
можно сделать до или после назначения. С практической точки зрения, может существовать компилятор, который делает то же самое, что и Java, но вы бы не хотели на него ставить.x = x++
работает следующим образом:x++
. Оценка этого выражения производит значение выражения (которое является значениемx
до приращения) и увеличиваетсяx
.x
, перезаписывая увеличенное значение.Итак, последовательность событий выглядит следующим образом (это фактический декомпилированный байт-код, созданный
javap -c
моими комментариями):Для сравнения
x = ++x
:источник
iinc
приращение переменной не увеличивает значение стека и не оставляет значения в стеке (в отличие от почти всех других арифметических операций). Вы можете добавить код, сгенерированный++x
для сравнения.Это происходит потому, что значение
x
вообще не увеличивается.эквивалентно
Объяснение:
Давайте посмотрим на байт-код для этой операции. Рассмотрим пример класса:
Теперь, запустив дизассемблер класса, мы получим:
Теперь Java VM основана на стеке, что означает, что для каждой операции данные будут помещаться в стек, а из стека данные будут появляться для выполнения операции. Существует также другая структура данных, обычно массив для хранения локальных переменных. Локальным переменным присваиваются идентификаторы, которые являются просто индексами массива.
Давайте посмотрим на мнемонику в
main()
методе:iconst_0
: Постоянное значение0
помещается в стек.istore_1
: Верхний элемент стека выталкивается и сохраняется в локальной переменной с индексом,1
который есть
x
.iload_1
: Значение в месте ,1
которое является значением ,x
которое является0
, выталкивается в стек.iinc 1, 1
: Значение в ячейке памяти1
увеличивается на1
. Такx
теперь и становится1
.istore_1
: Значение в верхней части стека сохраняется в ячейке памяти1
. То есть0
назначаетсяx
перезапись его увеличенного значения.Следовательно, значение
x
не изменяется, что приводит к бесконечному циклу.источник
++
), но переменная перезаписывается позже.int temp = x; x = x + 1; x = temp;
лучше не использовать тавтологию в вашем примере.Однако "
=
" имеет более низкий приоритет оператора, чем "++
".Поэтому
x=x++;
следует оценить следующим образомx
подготовлен к назначению (оценен)x
порядковымx
присваиваетсяx
.источник
++
имеет более высокий приоритет, чем=
в C и C ++, но оператор не определен в этих языках.Ни один из ответов, на которых нет места, так что здесь идет:
Когда вы пишете
int x = x++
, вы не присваиваетеx
себе новое значение, а присваиваетеx
возвращаемое значениеx++
выражения. Который, оказывается, является первоначальной ценностьюx
, как намекает в ответе Колин Кокрейн .Для удовольствия протестируйте следующий код:
Результат будет
Возвращаемым значением выражения является начальное значение
x
, равное нулю. Но позже, читая значениеx
, мы получаем обновленное значение, то есть единицу.источник
Это уже хорошо объяснили другие. Я просто включаю ссылки на соответствующие разделы спецификации Java.
х = х ++ это выражение. Java будет следовать порядку оценки . Сначала он вычислит выражение x ++, которое будет увеличивать x и устанавливать значение результата в предыдущее значение x . Затем он присваивает результат выражения переменной x. В конце х возвращается к своему предыдущему значению.
источник
Это утверждение:
оценивает так:
x
на стек;x
;x
из стека.Таким образом, значение не изменилось. Сравните это с:
который оценивается как:
x
;x
на стек;x
из стека.То, что вы хотите, это:
источник
x++
фрагмент кода.x++;
свое решение на,x=x; x++;
и вы делаете то, что, как вы утверждаете, делает исходный код.Ответ довольно прост. Это связано с тем, как оцениваются вещи.
x++
возвращает значение, аx
затем увеличиваетсяx
.Следовательно, значение выражения
x++
равно0
. Таким образом, вы назначаетеx=0
каждый раз в цикле. Конечно,x++
увеличивает это значение, но это происходит до назначения.источник
С http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
Для иллюстрации попробуйте следующее:
Который будет печатать 1 и 0.
источник
Вы эффективно получаете следующее поведение.
Идея состоит в том, что оператор постинкрементного увеличения (x ++) увеличивает значение этой переменной ПОСЛЕ возвращения значения для использования в уравнении, в котором он используется.
Изменить: немного добавить из-за комментария. Рассмотрите это как следующее.
источник
Вам не нужен машинный код, чтобы понять, что происходит.
Согласно определениям:
Оператор присваивания вычисляет выражение правой части и сохраняет его во временной переменной.
1.1. Текущее значение x копируется во временную переменную
1.2. х увеличивается сейчас.
Затем временная переменная копируется в левую часть выражения, что случайно равно x! Вот почему старое значение x снова копируется в себя.
Это довольно просто.
источник
Это потому, что в этом случае оно никогда не увеличивается.
x++
сначала будет использовать его значение перед увеличением, как в этом случае это будет выглядеть так:Но если вы
++x;
это сделаете, это увеличится.источник
Значение остается равным 0, потому что значение
x++
равно 0. В этом случае не имеет значения,x
увеличивается значение или нет, присваиваниеx=0
выполняется. Это перезапишет временное увеличенное значениеx
(которое было 1 для «очень короткого времени»).источник
x++
, а не для всего заданияx=x++;
Это работает так, как вы ожидаете от другого. Это разница между префиксом и постфиксом.
источник
Думайте о x ++ как о вызове функции, которая «возвращает» то, что X было до приращения (поэтому оно называется постинкрементом).
Таким образом, порядок операций:
1: кэшировать значение x до приращения
2: приращение x
3: возвращать кэшированное значение (x до его приращения)
4: возвращаемое значение присваивается x
источник
Когда ++ находится на правой стороне, результат возвращается до увеличения числа. Измените на ++ x, и это было бы хорошо. Java оптимизировал бы это для выполнения одной операции (присваивания x x), а не приращения.
источник
Ну, насколько я вижу, ошибка возникает из-за присваивания, переопределяющего увеличенное значение значением до увеличения, то есть оно отменяет приращение.
В частности, выражение «x ++» имеет значение «x» до приращения, а не «++ x», которое имеет значение «x» после приращения.
Если вы заинтересованы в изучении байт-кода, мы рассмотрим три строки:
7: iload_1 # Поместит значение 2-й локальной переменной в стек
8: iinc 1,1 # увеличит 2-ю локальную переменную на 1, обратите внимание, что стек остается нетронутым!
9: istore_1 # Извлечет вершину стека и сохранит значение этого элемента во 2-й локальной переменной
(Вы можете прочитать эффекты каждой инструкции JVM здесь )
Вот почему приведенный выше код будет зацикливаться бесконечно, в то время как версия с ++ x не будет. Байт-код для ++ x должен выглядеть совсем иначе, насколько я помню из компилятора Java 1.3, который я написал чуть больше года назад, байт-код должен выглядеть примерно так:
Так что, просто поменяв местами две первые строки, измените семантику так, чтобы значение, оставленное на вершине стека, после приращения (то есть «значение» выражения) было значением после приращения.
источник
Так:
В то время как
Так:
Конечно, конечный результат такой же, как просто
x++;
или++x;
на отдельной строке.источник
из-за вышеприведенного утверждения х никогда не достигает 3;
источник
Интересно, есть ли что-нибудь в спецификации Java, которая точно определяет поведение этого. (Очевидно, это утверждение означает, что мне лень проверять.)
Заметьте, из байт-кода Тома, ключевые строки 7, 8 и 11. Строка 7 загружает х в стек вычислений. Строка 8 увеличивает x. Строка 11 сохраняет значение из стека обратно в x. В обычных случаях, когда вы не присваиваете значения обратно самим себе, я не думаю, что была бы какая-либо причина, по которой вы не могли бы загрузить, сохранить, затем увеличить. Вы бы получили тот же результат.
Например, предположим, у вас был более нормальный случай, когда вы написали что-то вроде: z = (x ++) + (y ++);
Будь то сказано (псевдокод, чтобы пропустить технические детали)
или
должно быть неуместно. Я думаю, что любая реализация должна быть действительной.
Я был бы крайне осторожен в написании кода, который зависит от этого поведения. Это выглядит очень зависящим от реализации, между трещинами в спецификациях для меня. Единственный раз, когда это будет иметь значение - это если вы сделали что-то сумасшедшее, например, приведенный здесь пример, или если у вас были запущены два потока, и вы зависели от порядка вычисления в выражении.
источник
Я думаю, потому что в Java ++ имеет более высокий приоритет, чем = (присваивание) ... Так ли это? Посмотрите на http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...
Точно так же, если вы пишете x = x + 1 ... + имеет более высокий приоритет, чем = (назначение)
источник
++
имеет более высокий приоритет, чем=
в C и C ++, но оператор не определен.x++
Выражение имеет значениеx
.++
Часть влияет на стоимость после оценки , а не после заявления . такx = x++
эффективно переводится наисточник
Прежде чем увеличивать значение на единицу, значение присваивается переменной.
источник
Это происходит потому, что пост увеличивается. Это означает, что переменная увеличивается после вычисления выражения.
х теперь 10, а у 9, значение х перед его увеличением.
См. Больше в Определении Пост Прироста .
источник
x
/y
пример отличается от реального кода, и разница актуальна. Ваша ссылка даже не упоминает Java. Для двух языков это делает упомянуть, заявление в вопросе не определено.Проверьте код ниже,
выход будет,
post increment
означает увеличить значение и вернуть значение до приращения . Именно поэтому значениеtemp
является0
. Так что, еслиtemp = i
и это в цикле (за исключением первой строки кода). прямо как в вопросе !!!!источник
Оператор приращения применяется к той же переменной, к которой вы присваиваете. Это напрашивается на неприятности. Я уверен, что вы можете видеть значение вашей переменной x во время работы этой программы ... это должно прояснить, почему цикл никогда не заканчивается.
источник