Что такое х после «х = х ++»?


285

Что происходит (за занавесом), когда это выполняется?

int x = 7;
x = x++;

То есть, когда переменная увеличивается на единицу и присваивается себе в одном выражении? Я скомпилировал и выполнил это. xеще 7 даже после всего заявления . В моей книге говорится, что xувеличивается!


9
Попробуйте это: int x = 7; x = ++x;конечно, все еще ужасный код, вам не нужно переназначать. int x = 7; x++;достаточно.
stivlo

7
Это очень плохая практика, не увеличивайте переменную в той же строке, в которой вы ее используете.
Юсф

5
Я бы предпочел использовать x += 1, кроме, может быть, в петлях. for(int x=0; x<7; x++)
Свиш

2
@andyortlieb нет объекта, просто базовое значение.
Фортран

Ответы:


301

xдействительно увеличивается. Но вы назначаете старую ценность xобратно в себя.


x = x++;
  1. x++увеличивает xи возвращает его старое значение.
  2. x = присваивает старое значение себе

Таким образом, в конце концов, xвозвращается обратно к своему начальному значению.


3
Тогда что вы скажете о x = ++ x;
Хишам Мьюнер

3
@HishamMuneer xполучает приращение первых , прежде чем он прочитал в этом случае, так что вы в конечном итоге с x + 1.

@HishamMuneer Слишком поздно. Но я помещаю это здесь, потому что это может быть полезно для некоторых других людей, которые будут смотреть в будущее. Лучший способ понять эту проблему - посмотреть на ассемблерный код, созданный для x = x ++ и x = ++ x. Пожалуйста, смотрите ответ Thinkingcap также.
nantitv

Я знаю, что это очень старый, но у меня есть вопрос. Гарантируется ли вышеуказанный порядок работы стандартом? Возможно ли, что присвоение выполняется до приращения?
Изумрудное оружие

@EmeraldWeapon Это определено в Java. Только в C / C ++ вы видите такие махинации.
Мистик

385
x = x++;

эквивалентно

int tmp = x;
x++;
x = tmp;

46
Lol, Yay для рекурсивных определений. Вы, вероятно, должны были сделать x=x+1вместоx++
user606723

8
@ user606723: Нет. Я имел в виду целое утверждение x = x++, а не только приращение поста x++.
Принц Джон Уэсли

20
Я не думаю, что это все, что полезно без дальнейших объяснений. Например, это неправда, что x = ++x;также эквивалентно int tmp = x; ++x; x = tmp;, поэтому по какой логике мы можем сделать вывод, что ваш ответ правильный (какой он есть)?
KVB

4
еще яснее это в asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker

3
@forker: Я думаю, было бы яснее, если бы вы использовали инструкции по сборке, относящиеся к процессору, который использует Майкл;)
Карл

258

Заявление:

x = x++;

эквивалентно:

tmp = x;   // ... this is capturing the value of "x++"
x = x + 1; // ... this is the effect of the increment operation in "x++" which
           //     happens after the value is captured.
x = tmp;   // ... this is the effect of assignment operation which is
           //     (unfortunately) clobbering the incremented value.

Короче говоря, заявление не имеет никакого эффекта.

Ключевые моменты:

  • Значение выражения увеличения / уменьшения Postfix - это значение операнда до того, как произойдет увеличение / уменьшение. (В случае формы префикса значением является значение операнда после операции)

  • RHS выражения присваивания полностью оценивается (включая любые приращения, уменьшения и / или другие побочные эффекты) до того, как значение присваивается LHS.

Обратите внимание, что в отличие от C и C ++, порядок вычисления выражения в Java полностью указан, и нет места для изменения, зависящего от платформы. Компиляторам разрешено переупорядочивать операции только в том случае, если это не изменит результат выполнения кода с точки зрения текущего потока. В этом случае компилятору будет разрешено оптимизировать весь оператор, поскольку можно доказать, что он не используется.


Если это еще не очевидно:

  • "х = х ++;" почти наверняка ошибка в любой программе.
  • ОП (для первоначального вопроса!), Вероятно, означало «x ++;» а не "x = x ++;".
  • Утверждения, которые комбинируют автоматическое увеличение / уменьшение и присваивание для одной и той же переменной, трудно понять, и поэтому их следует избегать независимо от их правильности . Просто нет необходимости писать такой код.

Надеемся, что программы проверки кода, такие как FindBugs и PMD, будут отмечать код как подозрительный.


7
Как примечание, OP, вы, вероятно, хотите просто сказать x++вместо x = x++.
Джон Ньюмуис

3
Правильно, но, возможно, следует подчеркнуть, что приращение происходит после оценки выражения правой руки, но предварительно присваивается левой стороне, отсюда и очевидное «перезапись»
Богемский

2
это похоже на одну из тех программных проблем в старшей школе ... хорошо, чтобы прояснить твои основы!
kumarharsh

1
@Alberto - Приятно слышать, что вы не воспринимаете «экспертные» высказывания как «евангельскую истину». Тем не менее, лучший способ проверить, что я сказал, это обратиться к JLS. Ваш тест компиляции / декомпиляции показывает, что то, что я сказал, действительно для одного компилятора Java. Другие могут (гипотетически) вести себя по-другому ... за исключением того, что JLS не позволяет этого.
Стивен С.

4
Просто к вашему сведению: это было первоначально размещено на другой вопрос, который был закрыт как дубликат этого и теперь был объединен.
Shog9

33
int x = 7;
x = x++;

Он имеет неопределенное поведение в C и для Java см. Этот ответ . Это зависит от компилятора, что произойдет.


4
Нет, это не зависит от компилятора в соответствии с цитируемым вами ответом - пожалуйста, отредактируйте - -1, пока
Mr_and_Mrs_D

@Mr_and_Mrs_D Тогда это зависит от чего?
Mac

2
Поведение не определено только для C_. Даже если сказать, что это зависит от компилятора, вводит в заблуждение - это означает, что компилятор должен как-то определять это поведение. Я возвращаю свой голос, но рассмотрю возможность редактирования вашего ответа - отредактируйте: ой, я не могу - вы должны сначала отредактировать его: D
Mr_and_Mrs_D

16

Конструкция like x = x++;показывает, что вы, вероятно, неправильно понимаете, что ++делает оператор:

// original code
int x = 7;
x = x++;

Давайте перепишем это, чтобы сделать то же самое, основываясь на удалении ++оператора:

// behaves the same as the original code
int x = 7;
int tmp = x; // value of tmp here is 7
x = x + 1; // x temporarily equals 8 (this is the evaluation of ++)
x = tmp; // oops! we overwrote y with 7

Теперь давайте перепишем это, чтобы сделать (что я думаю), что вы хотели:

// original code
int x = 7;
x++;

Тонкость здесь заключается в том, что ++оператор изменяет переменнуюx , в отличие от такого выражения, как x + x, которое будет вычислять значение int, но сама переменная останется xнеизменной. Рассмотрим такую ​​конструкцию, как почтенный forцикл:

for(int i = 0; i < 10; i++)
{
    System.out.println(i);
}

Заметили i++там? Это тот же оператор. Мы можем переписать этот forцикл следующим образом, и он будет вести себя так же:

for(int i = 0; i < 10; i = i + 1)
{
    System.out.println(i);
}

Я также рекомендую не использовать ++оператор в больших выражениях в большинстве случаев. Из-за тонкости того, когда она изменяет исходную переменную до пост-приращения ( ++xи x++, соответственно), очень легко вводить тонкие ошибки, которые трудно отследить.


13

Согласно байт-коду полученному из файлов классов,

Оба назначения увеличивают х, но разница заключается во времени when the value is pushed onto the stack

В Case1 , Push происходит (а затем назначается позже) до приращения (по сути, это означает, что ваш прирост ничего не делает)

В Case2 , сначала происходит инкремент (делая его 8), а затем помещается в стек (а затем назначается х)

Случай 1:

int x=7;
x=x++;

Байт-код:

0  bipush 7     //Push 7 onto  stack
2  istore_1 [x] //Pop  7 and store in x
3  iload_1  [x] //Push 7 onto stack
4  iinc 1 1 [x] //Increment x by 1 (x=8)
7  istore_1 [x] //Pop 7 and store in x
8  return       //x now has 7

Случай 2:

int x=7; 
x=++x;

Байт код

0  bipush 7     //Push 7 onto stack
2  istore_1 [x] //Pop 7 and store in x
3  iinc 1 1 [x] //Increment x by 1 (x=8)
6  iload_1  [x] //Push x onto stack
7  istore_1 [x] //Pop 8 and store in x
8  return       //x now has 8
  • Стек здесь относится к стеку операндов, локальный: x индекс: 1 тип: int

Можете ли вы объяснить ваш ответ в деталях.
Нихар

Просьба взглянуть на ссылочную ссылку и комментарии
evenprime


8

Постинкрементный оператор работает следующим образом:

  1. Сохранить предыдущее значение операнда.
  2. Увеличьте значение операнда.
  3. Вернуть предыдущее значение операнда.

Итак, утверждение

int x = 7;
x = x++; 

будет оцениваться следующим образом:

  1. х инициализируется значением 7
  2. Оператор постинкрементного хранения сохраняет предыдущее значение x, т. е. 7 для возврата.
  3. Увеличивает х, так что теперь х равен 8
  4. Возвращает предыдущее значение x, то есть 7, и оно присваивается обратно x, поэтому x снова становится 7

Таким образом, x действительно увеличивается, но поскольку x ++ присваивает результат обратно x, значение x переопределяется на предыдущее значение.


Но в msvc x равен 8. Да в gcc и clang x равен 7.
Summer Sun

7

Инкремент происходит после вызова x, поэтому x по-прежнему равен 7. ++ x будет равен 8 при вызове x


7

Когда вы переназначаете значение, xоно все еще равно 7. Попробуйте, x = ++xи вы получите 8, иначе сделайте

x++; // don't re-assign, just increment
System.out.println(x); // prints 8

6

потому что x ++ увеличивает значение ПОСЛЕ присвоения его переменной. так далее и во время выполнения этой строки:

x++;

переменная x будет по-прежнему иметь исходное значение (7), но снова использовать x в другой строке, например

System.out.println(x + "");

даст вам 8.

если вы хотите использовать увеличенное значение x в вашем операторе присваивания, используйте

++x;

Это увеличит x на 1, ТО затем присвоит это значение переменной x.

[Edit] вместо x = x ++, это просто x ++; первый присваивает исходное значение x самому себе, поэтому он фактически ничего не делает в этой строке.


Тот, который говорит, что увеличивается после присваивания, и тот, который говорит, что он напечатает 8. Он увеличивает перед присваиванием и печатает 7.
R. Martinho Fernandes

если x изначально равен 7, System.out.println (String.valueOf (x ++)); печатает 7. Вы уверены, что мы говорим об одном и том же языке программирования?
Иосиф

Да, я. Этот ideone.com/kj2UU не печатает 8, как утверждает этот ответ.
Р. Мартиньо Фернандес

да я был неправ x = x ++ сначала назначит 7 для x перед увеличением x. поскольку x ++ (который сам по себе является присваиванием) разрешается первым до x = (что угодно), за ним будет следовать значение, присвоенное x в x = (что бы то ни было). извини, я этого не видел.
Иосиф

1
На самом деле, приращение - это первое, что происходит. ideone.com/xOIDU
Р. Мартиньо Фернандес

4

Что происходит, когда int x = 7; x = x++; ?

ans -> x++означает сначала использовать значение x для выражения, а затем увеличить его на 1.
Это то, что происходит в вашем случае. Значение x в RHS копируется в переменную x в LHS, а затем значениеx увеличивается на 1.

Аналогично ++x означает ->увеличение значения х сначала на единицу, а затем использовать в выражении.
Так что в вашем случае, если вы это сделаете, x = ++x ; // where x = 7
вы получите значение 8.

Для большей ясности попытайтесь выяснить, сколько операторов printf выполнит следующий код

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend

не правильно "Значение x в RHS копируется в переменную x в LHS, а затем значение x увеличивается на 1" - это будет x8, но это 7 - приращение происходит между чтением и присваиванием
user85421

3

++xэто постинкремент ->x увеличивается перед использованием
x++это постинкремент ->x увеличивается после использования

int x = 7; -> x get 7 value <br>
x = x++; -> x get x value AND only then x is incremented

1

Так что это означает: x++не равноx = x+1

так как:

int x = 7; x = x++;
x is 7

int x = 7; x = x = x+1;
x is 8

и теперь это кажется немного странным:

int x = 7; x = x+=1;
x is 8

очень зависит от компилятора!


2
кто сказал, что это было равным в первую очередь?
Фортран

1
Если бы я был тобой, я бы сразу уничтожил эти книги. XD В любом случае это было бы как (x = x + 1, x-1)в C, где допускаются выражения, разделенные запятыми.
Фортран

3
@fortran: Хорошо, в моей десятилетней копии «Языка программирования Java, третье издание» на стр. 159 написано «Выражение i ++ эквивалентно i = i + 1, за исключением того, что i вычисляется только один раз». Кто сказал во-первых, Джеймс Гослинг, кажется, эта часть этой редакции спецификации Java чрезвычайно расплывчата и плохо определена; я полагаю, что более поздние редакции очистили язык, чтобы более четко выразить фактическую семантику операторов.
Эрик Липперт

2
@fortran: «кроме того, что i оценивается только один раз», стандарт пытается передать, что выражение типа «M (). x ++» вызывает M () только один раз. Менее расплывчатая и более точная формулировка подчеркнула бы, что существует разница между оценкой i как переменной для определения ее места хранения - что подразумевается под "оценкой только один раз" - и чтением или записью в это место хранения - - любой из которых может быть разумным, но неправильным толкованием «оцененного». Ясно, что место хранения должно быть как прочитанным, так и записанным!
Эрик Липперт

1
"очень зависит от компилятора" - совсем нет!
Стивен С

-1

х = х ++;

Это оператор после увеличения. Это следует понимать как «Использовать значение операнда и затем увеличивать операнд».

Если вы хотите, чтобы произошло обратное, то есть «Увеличьте операнд, а затем используйте значение операнда», вы должны использовать оператор предварительного увеличения, как показано ниже.

х = ++ х;

Этот оператор сначала увеличивает значение x на 1, а затем присваивает значение обратно x.


-1

Я думаю, что этот спор можно решить, не вдаваясь в код и просто подумав.

Рассмотрим i ++ & ++ i как функции, скажем Func1 & Func2.

Теперь я = 7;
Func1 (i ++) возвращает 7, Func2 (++ i) возвращает 8 (это знают все). Внутри обе функции увеличивают i до 8, но они возвращают разные значения.

Поэтому я = i ++ вызывает функцию Func1. Внутри функции i увеличивается до 8, но по завершении функция возвращает 7.

Таким образом, в конечном итоге 7 получает я. (Итак, в итоге я = 7)


2
Здесь нет действительного "противоречия". Код явно ведет себя определенным образом, и поведение соответствует JLS. Любой, кто думает, что он ведет себя по-другому, либо не пробовал, либо они введены в заблуждение. (Это немного напоминает о том , что 7 х 7 является 49 «спорным» , когда кто - то забывает их раз таблицы ...)
Stephen C

-2

Это потому, что вы использовали оператор постинкрементного. В этой следующей строке кода

x = x++;

Что происходит, вы присваиваете значение x для x. x ++ увеличивает x после того, как значение x присвоено x. Вот как работают операторы постинкрементной обработки. Они работают после того, как заявление было выполнено. Таким образом, в вашем коде x возвращается первым, а затем постепенно увеличивается.

Если вы сделали

x = ++x;

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.