Що таке x після “x = x ++”?


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;
Hisham Muneer

3
@HishamMuneer xспочатку збільшується до того, як його прочитають у такому випадку, тож ви закінчите x + 1.

@HishamMuneer Ще пізно. Але я викладаю це, тому що це може бути корисним для інших людей, які шукатимуть у майбутньому. Найкращий спосіб вирішити цю проблему - дивитися на код складання, створений для x = x ++ і x = ++ x. Будь ласка, дивіться також відповідь Thinkingcap.
nantitv

Я знаю, що це дуже старе, але у мене є питання. Чи гарантується вищезазначений порядок експлуатації стандартом? Чи можливо, що завдання виконується до збільшення?
Смарагдова зброя

@EmeraldWeapon Це визначено в Java. Лише в С / С ++ ви бачите такого роду шнанігани.
Містичний

385
x = x++;

еквівалентно

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

46
Lol, так для рекурсивних визначень. ви, мабуть, мали б зробити це 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
форкер

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 ++;" а не "x = x ++;".
  • Твердження, що поєднують автоматичне включення / зменшення та призначення на одній змінній, важко зрозуміти, і тому їх слід уникати незалежно від їх правильності . Просто не потрібно писати такий код.

Сподіваємось, перевіряючі коди, такі як FindBugs та PMD, позначать такий код як підозрілий.


7
Як бічна примітка, ОП, ви, мабуть, маєте на увазі просто сказати x++замість цього x = x++.
Джон Ньюмуас

3
Правильно, але, можливо, підкресліть, що приріст трапляється після правої оцінки вираження, але попередньо присвоєння лівій частині, звідси очевидне "перезапис"
богем

2
це здається одним із тих твістерів програмування середньої школи ... добре, щоб прояснити свої основи!
кумархарш

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

Така конструкція 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, Increment відбувається перший ( що робить його 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
  • Стек тут відноситься до стека Operand, локальний: x індекс: 1 тип: int

Чи можете ви, будь ласка, пояснити свою відповідь детально.
Ніхар



8

Оператор поштового збільшення працює наступним чином:

  1. Зберегти попереднє значення операнда.
  2. Збільшення значення операнда.
  3. Повернути попереднє значення операнда.

Отже, твердження

int x = 7;
x = x++; 

оцінювали б так:

  1. x ініціалізується зі значенням 7
  2. Оператор поштового збільшення зберігає попереднє значення x, тобто 7 для повернення.
  3. Зростає х, тому зараз х дорівнює 8
  4. Повертає попереднє значення x, тобто 7, і воно присвоюється назад x, тому x знову стає 7

Так x дійсно збільшується, але оскільки x ++ присвоює результат назад до x, то значення x перевищується на його попереднє значення.


Але в msvc x це 8. Так, у gcc та clang x - 7.
Літнє Сонце,

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++;

varialbe x як і раніше буде мати початкове значення (7), але знову використовуючи x в іншому рядку, наприклад

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

дасть вам 8.

якщо ви хочете використовувати збільшене значення x у своїй заяві про призначення, використовуйте

++x;

Це збільшить х на 1, ТОГО призначить це значення змінній x.

[Редагувати] замість x = x ++, це просто x ++; колишнє присвоює первісне значення x, тому воно фактично нічого не робить у цьому рядку.


Той, який каже, що він збільшується після присвоєння, і той, який говорить, що він надрукує 8. Він збільшується до призначення, і він надрукує 7.
Р. Мартіньо Фернандес

якщо 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 = ++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++, після наростання ->х збільшується після використання

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, за винятком того, що я оцінюється лише один раз ". Хто сказав це в першу чергу? Джеймс Гослінг, здається. Ця частина цього видання специфікації Java надзвичайно розпливчаста і неякісна; я припускаю, що пізніші видання очистили мову, щоб чіткіше виразити фактичну семантику оператора.
Ерік Ліпперт

2
@fortran: "за винятком того, що я оцінюється лише один раз", стандарт намагається донести, що вираз типу "M (). x ++" викликає M () лише один раз. Менш розпливчасте і точніше формулювання підкреслить, що існує різниця між оцінкою i як змінної для визначення місця її зберігання - що означає "тут оцінюється лише один раз" - читанням чи записом до цього місця зберігання - - будь-яке з них може бути розумним, але неправильним тлумаченням "оцінюваного". Очевидно, що місце зберігання має бути прочитане та записане!
Ерік Ліпперт

1
"дуже залежить від компілятора" - зовсім не!
Стівен C

-1

x = x ++;

Це оператор після збільшення. Це слід розуміти як "Використовувати значення операнду, а потім збільшувати операнд".

Якщо ви хочете, щоб сталося зворотне, тобто "Збільшення операнда, а потім використання значення операнда", ви повинні використовувати оператора попереднього збільшення, як показано нижче.

x = ++ x;

Цей оператор спочатку збільшує значення x на 1, а потім присвоює значення назад до x.


-1

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

Розглянемо i ++ та ++ i як функції, скажімо, Func1 та Func2.

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

Отже, i = i ++ викликає функцію Func1. Всередині функції i збільшується до 8, але після завершення функція повертає 7.

Таким чином, в кінцевому рахунку 7 отримує i. (Отже, зрештою, i = 7)


2
Тут немає вагомих «суперечок». Код демонстративно поводиться певним чином, а поведінка відповідає JLS. Той, хто думає, що він поводиться по-різному, або не пробував цього, або його вводили в оману. (Це трохи схоже на те, що 7 x 7 - 49 - це "суперечливо", коли хтось забув свої таблиці часу ...)
Стівен 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.