Для i = 0, чому (i + = i ++) дорівнює 0?


253

Візьміть такий код (який можна використовувати як консольний додаток):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

Результат i- 0. Я очікував 2 (як це зробили деякі мої колеги). Ймовірно, компілятор створює якусь структуру, що призводить до iнуля.

Причина, яку я очікував 2, полягає в тому, що, в моєму думці, спочатку слід оцінювати твердження правої руки, збільшуючи i на 1, ніж це додано до i. Оскільки я вже 1, він додає 1 до 1. Тож 1 + 1 = 2. Очевидно, що це не те, що відбувається.

Чи можете ви пояснити, що робить компілятор або що відбувається під час виконання? Чому результат дорівнює нулю?

Якийсь тип відмови від відповідальності: я абсолютно усвідомлюю, що ви не будете (і, мабуть, не повинні) використовувати цей код. Я знаю, що ніколи не буду. Тим не менш, мені здається цікавим дізнатися, чому він діє таким чином і що саме відбувається.


57
не повинен очікуваний результат бути 1? i (0) + = i ++ (1), тому 0 + = 1 = 1
звільнення

11
Це працює так, як очікувалося
Steve a D

177
Скільки варіантів цього питання буде задано?
mowwwalker

20
Попередній показник збільшить значення перед тим, як зробити дію i + = ++, я дам вам 1
Pierluc SS

21
Чому всі зосереджуються на до-після підвищення? «Дивна» Справа в тому , що значення з iлівого боку +=знаходиться « в кеші» перед правою оцінюється. Це контр-інтуїтивно, оскільки це, наприклад, вимагало б операції копіювання, якби iце був об'єкт. (Будь ласка, не розумійте мене: я абсолютно погоджуюся на те, що 0це правильна та відповідна стандартам відповідь.)
JohnB

Відповіді:


425

Це:

int i = 0;
i += i++

Ви можете бачити, як ви це робите (наступне - грубе надмірне спрощення):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

Що насправді відбувається, це більше пов'язане з цим - подивіться на MSDN, 7.5.9 операторів збільшення та скорочення Postfix :

Операція виконання часу збільшення або зменшення постфікса форми x ++ або x-- складається з наступних етапів:

  • Якщо х класифіковано як змінну:

    • x оцінюється для отримання змінної.
    • Значення x зберігається.
    • Вибраний оператор викликається збереженим значенням x як його аргументом.
    • Значення, повернене оператором, зберігається в місці, заданому оцінкою x.
    • Збережене значення x стає результатом операції.

Зауважте, що через порядок пріоритетності постфікс ++виникає раніше += , але результат в кінцевому підсумку не використовується (як використовується попереднє значення i).


Більш ретельне розкладання i += i++частин вимагає того, щоб вони знали, що обидва +=і ++не є атомними (тобто жодна не є однією операцією), навіть якщо вони виглядають такими, якими вони є. Спосіб їх реалізації включає в себе тимчасові змінні, копії iдо початку операцій - по одній для кожної операції. (Я буду використовувати назви iAddта iAssignдля тимчасових змінних, що використовуються для ++та +=відповідно).

Отже, ближчим наближенням до того, що відбувається було б:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;

3
@ Oded ++Операція виконується до завершення оцінки твердження. Так +=переписується значення. Це сталося?
Аніруд Рамананат

6
@Oded фактично ІТС: int i = 0; i = i + 1; (postfix) i = 0; (assignment). Якщо ви використовували i інше в цьому твердженні, воно буде оцінювати до 1 в той час.
drch

@Cthulhu - По суті. Відповідь на DTB поглиблюється в деталі.
Одід

6
Я не купую цього. Відповідь @yoriy набагато точніша. По-перше, у своїй відповіді ви говорите, що останній рядок був би i+1тоді, коли він повинен бути i=i+1. Хіба це не те, що i++є?
відійти

3
Перша частина відповіді є зайвою. Ваш останній зразок коду міг би зробити це IMHO. Хоча +1.
corazza

194

Розбирання запущеного коду:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Еквівалентний код

Він компілюється в той самий код, що і наступний код:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

Розбирання другого коду (лише щоб довести, що вони однакові)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Відкриття вікна розбирання

Більшість людей не знають або навіть не пам’ятають, що вони можуть бачити остаточний код складання пам’яті, використовуючи вікно розбирання Visual Studio . Він показує машинний код, який виконується, це не CIL.

Використовуйте це під час налагодження:

Debug (menu) -> Windows (submenu) -> Disassembly

Отже, що відбувається з postfix ++?

Postfix ++ говорить про те, що ми хотіли б збільшити значення операнду після оцінки ... що всі знають ... те, що трохи заплутує, - це значення "після оцінки" .

Отже, що означає "після оцінки" означає:

  • інші звичаї операнду, в тому ж рядку коду, повинні бути порушені:
    • a = i++ + i другий i впливає на приріст
    • Func(i++, i) другий я зачіпається
  • інші звичаї на тій же лінії поваги оператора короткого замикання , як ||і &&:
    • (false && i++ != i) || i == 0 третій i не впливає на i ++, оскільки він не оцінюється

Отже, який сенс: i += i++; :?

Це те саме, що i = i + i++;

Порядок оцінювання:

  1. Зберігати i + i (тобто 0 + 0)
  2. Приріст i (i стає 1)
  3. Призначте значення кроку 1 до i (я стає 0)

Не те, що приріст відкидається.

Що це означає: i = i++ + i;?

Це не те саме, що попередній приклад. 3-йi приріст впливає .

Порядок оцінювання:

  1. Магазин i (тобто 0)
  2. Приріст i (i стає 1)
  3. Зберігати значення кроку 1 + i (тобто 0 + 1)
  4. Призначте значення кроку 3 i i (я стає 1)

22
+ 1 ++ - для чистої жорсткої дисекції. Чак Норіс був би гордий :) Я думаю, ви робите припущення, що ОП працює на Intel, а не на
монопорті,

19
C # має чітко визначений порядок оцінки виразу, і об'єктний код просто реалізує цей порядок. Виведення машинного коду не є причиною чи поясненням порядку замовлення.
Каз

8
Машинний код дозволяє легко зрозуміти, як порядок оцінювання реалізується ІМО.
Кевін

5
@StuartLC Я бачу, що ти там робив. Хоч сором за цей відкинутий ансамбль.
Steffan Donal

2
a++ + aце не те саме, що a + a++тому, що це вже не чиста математика. Закон комутативності в алгебрі не враховує можливості змінних змінних значень посередині через вираз. Математика чітко відображає програмування лише тоді, коли програмування є функціональним програмуванням. І навіть тоді, через обмеження представництва. Наприклад, номери з плаваючою комою іноді поводяться як дійсні, а іноді - ні. Навіть без побічних ефектів, закони комутативності та асоціативності, які застосовуються до реальних чисел у математиці, розбиваються на числа з плаваючою комою.
Каз

61
int i = 0;
i += i++;

оцінюється наступним чином:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

тобто iзмінюється двічі: один раз i++виразом і один раз +=твердженням.

Але операнди +=твердження є

  • значення iперед оцінкою i++(зліва +=) та
  • значення iперед оцінкою i++(права частина +=).

Ах, це фантастичне пояснення. Нагадує мені, коли я працював на калькуляторі на основі стека, використовуючи зворотні польські позначення.
Натан

36

По-перше, i++повертається 0. Потім iзбільшується на 1. Нарешті iвстановлюється початкове значення, iяке дорівнює 0 плюс i++повернене значення , яке також дорівнює нулю. 0 + 0 = 0.


2
Але це i += i++;не i = i++;так, тому значення i++(0) додається до i, а не " iвстановлюється до i++повернутого значення ". Тепер питання полягає в тому, що при додаванні i++поверненого значення iбуде iзбільшену величину чи не нарощене значення? Відповідь, мій друже, написана у специфікаціях.
Даніель Фішер

Правда, я це виправлю. Але так чи інакше i = 0спочатку i += somethingрівнозначно тому, i = 0 + somethingщо є i = something.
Чжон

32

Це просто зліва направо, знизу вгору оцінка абстрактного синтаксичного дерева. Концептуально дерево виразу рухається зверху вниз, але оцінка розгортається, коли рекурсія спливає назад дерево з дна.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

Оцінка починається з розгляду кореневого вузла +=. Це головна складова виразу. Лівий операнд +=необхідно оцінити, щоб визначити місце, де ми зберігаємо змінну, і отримати попереднє значення, яке дорівнює нулю. Далі слід оцінити праву сторону.

Права сторона - ++оператор після збільшення . Він має один операнд, iякий оцінюється і як джерело значення, і як місце, де варто зберігати значення. Оператор оцінює i, знаходить 0і, отже, зберігає 1в цьому місці. Він повертає попереднє значення,0 відповідно до його семантики повернення попереднього значення.

Тепер управління повертається до +=оператора. Тепер він має всю інформацію для завершення роботи. Він знає місце, де зберігати результат (місце зберігання i), а також попереднє значення, і він має значення, яке додається до попереднього значення, а саме 0. Отже, iзакінчується нулем.

Як і Java, C # переосмислив дуже асинічний аспект мови С, встановивши порядок оцінювання. Зліва направо, знизу вгору: найочевидніший порядок, який, можливо, очікують кодери.


+1: Я згоден з тобою, за винятком того, що кожен кодер очікує, що ... я очікував, що це буде те саме, що щось подібне: SetSum(ref i, Inc(ref i))з int SetSum(ref int a, int b) { return a += b; }і int Inc(ref int a) { return a++; }... звичайно, я більше цього не очікую.
Мігель Анжело

Також те , що я очікував, є непослідовним! Це не було б рівним Set(ref i, Sum(i, Inc(ref i)))с int Set(ref int a, int b) { return a = b; }і int Sum(int a, int b) { return a + b; }.
Мігель Анжело

Дякую; ви натякаєте на недолік / незавершеність моєї відповіді, яку я маю виправити.
Каз

Проблема SetSumполягає в тому, що він не оцінює лівий операнд i, а лише приймає його адресу, тому це не еквівалентно повному оцінці зліва направо на операнд. Вам потрібно щось подібне SetSum(ref i, i, PostInc(ref i)). Другий аргумент SetSum- це значення, яке потрібно додати, і ми просто використовуємо iдля визначення попереднього значення i. SetSumпросто int SetSum(ref int dest, int a, int b) { return dest = a + b; }.
Каз

Плутанина відбувається (принаймні для мене) з оператором + =, тому що оператор присвоєння має праворуч-ліворуч оцінку (наприклад, a = b = c = d) ... тож можна уявити, що + = слідує тому ж правилу, як атомна операція (як я робив із моїм методом SetSum) ... але те, що відбувається насправді, це те, що C # перекладається a += bна a = a + b... показуючи, що + = оператор не є атомним ... це просто синтаксичний цукор.
Мігель Анжело

30

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


17

Метод післяпримноження виглядає приблизно так

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

Таким чином, коли ви телефонуєте i++, iце приріст, але вихідне значення повертається у вашому випадку, це 0 повертається.


12

Проста відповідь

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;

12

i ++ означає: повернути значення i THEN збільшення його.

i + = i ++ означає: Візьміть поточне значення i. Додайте результат i ++.

Тепер додамо в стартовій умові i = 0. i + = i ++ зараз оцінюється так:

  1. Яке поточне значення i? Це 0. Зберігайте його, щоб ми могли додати до нього результат i ++.
  2. Оцініть i ++ (оцінює до 0, оскільки це поточне значення i)
  3. Завантажте збережене значення та додайте до нього результат кроку 2. (додайте 0 до 0)

Примітка. Наприкінці кроку 2 значення i є фактично 1. Однак на кроці 3 ви відкидаєте його, завантажуючи значення i до його збільшення.

На відміну від i ++, ++ i повертає збільшене значення.

Тому я + = ++ я дав би вам 1.


Це допомога Full
sonsha

11

Оператор приросту після виправлення ++, надає змінній значення у виразі, а потім зробить присвоєний вамиi приріст повернув нульове (0) значення знову, що перезаписує збільшене (1) , так що ви отримуєте нуль. Ви можете прочитати більше про оператора приросту в Операторі ++ (MSDN).


8

i += i++;дорівнюватиме нулю, тому що це робить ++згодом.

i += ++i; зробить це раніше


4
Якщо це станеться ++згодом, я очікую, що результат буде 1.
comecme

8

Постфікс ++ оцінюється iперед його збільшенням і +=оцінюється лише iодин раз.

Отже, 0 + 0 = 0, як iоцінюється та використовується до його збільшення, як використовується формат постфіксу ++. Щоб отримати iінкремент спочатку, використовуйте форму префікса ( ++i).

(Крім того, лише примітка: ви повинні отримати лише 1, оскільки 0 + (0 + 1) = 1)

Посилання: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)


8

Що C # робить, і "чому" плутанини

Я також очікував, що значення буде 1 ... але деякі дослідження з цього питання уточнили деякі моменти.

Розгляньте наступні методи:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

Я очікував, що i += i++це буде те саме SetSum(ref i, Inc(ref i)). Значення i після цього твердження дорівнює 1 :

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

Але тоді я прийшов до іншого висновку ... i += i++насправді такий же, як i = i + i++... тому я створив ще один подібний приклад, використовуючи ці функції:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

Після виклику цього Set(ref i, Sum(i, Inc(ref i)))значення i дорівнює 0 :

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

Це не тільки пояснює, що C # робить ..., а й чому багато людей плутаються з цим ... в тому числі і я.


2
Будь ласка, додайте це до своєї оригінальної відповіді, це не додасть жодної користі, щоб це було окремою відповіддю.
casperOne

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

7

Я добре пам'ятаю про це наступне:

Якщо ++стоїть після виразу, він повертає значення, яке було раніше . Отже наступний код

int a = 1;
int b = a++;

є 1, тому що aбув 1, перш ніж він збільшився, коли ++стояв після a . Люди називають це повідомлення про виправлення повідомлення . Існує також нотація попереднього виправлення, де все відбувається прямо навпаки: якщо ++стоїть раніше , вираз повертає значення, яке воно є після операції:

int a = 1;
int b = ++a;

b тут два.

Отже, для вашого коду це означає

int i = 0;
i += (i++);

i++повертає 0 (як описано вище), так 0 + 0 = 0.

i += (++i); // Here 'i' would become two

Скотт Майєрс описує різницю між цими двома позначеннями в "Ефективному програмуванні на C ++". Внутрішньо i++(postfix) запам'ятовує значення iбуло, і викликає префікс-notation ( ++i) і повертає старе значення i,. Ось чому ви повинні використовувати ЗАВЖДИ ++iв forпетлях (хоча я думаю , що всі сучасні компілятори переводять i++до ++iв forпетлях).


1
Я тестував int i = 0; i += (++i), і iвстановлюється один, а не два. Для мене теж має сенс, оскільки використання префікса замість постфікса не змінює факту, що, якщо ви його i += (++i)випишете i = i + (++i), значення iоцінюється раніше ++i, в результаті чого i = 0 + (++i)і в кінцевому рахунку i = 0 + 1.
Wutz

6

Єдина відповідь на ваше запитання, яка є правильною, - це тому, що вона не визначена.

Добре, перш ніж ви мене спалите ..

Ви всі відповіли, чому i+=i++це нормально і логічно це призвести i=0.

Я спокусився проголосувати за кожну 1 вашу відповідь, але врахування репутації, яку я підрахував, буде занадто високою.

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

Але який результат ?? це інтуїтивний результат - чи прийнятний результат ??

Кожен з вас бачив "голого царя" і якось сприймав його як раціонального царя.

Ви всі НЕПРАВИЛЬНІ!

i+=i++;результат 0не визначений.

помилка в механізмі оцінювання мови, якщо ви .. чи ще гірше! помилка в дизайні.

хочете доказ? звичайно, ти хочеш!

int t=0; int i=0; t+=i++; //t=0; i=1

Тепер це ... інтуїтивно зрозумілий результат! тому що ми спершу оцінили tприсвоєне йому значення і лише після оцінки та присвоєння у нас відбулася поштова операція - раціонально чи не так?

чи раціонально це: i=i++і i=iдати такий же результат i?

в той час t=i++і t=iмають різні результатиi .

Операція публікації - це те, що повинно відбутися після оцінки твердження.
Тому:

int i=0;
i+=i++;

Має бути те саме, якби ми писали:

int i=0;
i = i + i ++;

і тому те саме, що:

int i=0;
i= i + i;
i ++;

і тому те саме, що:

int i=0;
i = i + i;
i = i + 1;

Будь-який результат, який не 1вказує на помилку в компіляторі або помилку в мовній конструкції, якщо ми йдемо з раціональним мисленням - однак MSDN та багато інших джерел говорять нам "ей - це не визначено!"

Тепер, перш ніж продовжувати, навіть цей набір прикладів я не підтримував і не визнавав ніхто. Однак це, згідно інтуїтивному та раціональному способу, повинно було бути результатом.

Кодер не повинен знати про те, як пишеться або перекладається збірка!

Якщо це написано таким чином, що не буде поважати мовних визначень - це помилка!

І для закінчення я скопіював це з операторів Wikipedia, Increment і decrement :
Оскільки оператор збільшення / зменшення модифікує свій операнд, використання такого операнда більше одного разу в одному і тому ж виразі може давати невизначені результати . Наприклад, у таких виразах, як x - ++ x, незрозуміло, в якій послідовності слід виконувати оператори віднімання та збільшення. Такі ситуації ще більше погіршуються, коли компілятор застосовує оптимізацію, що може призвести до того, що порядок виконання операцій буде іншим, ніж те, що планував програміст.

І таким чином.

Правильна відповідь полягає в тому, що ЦЕ ПОТРІБНО НЕ ВИКОРИСТОВУЄТЬСЯ! (як це НЕ ВИЗНАЧЕНО!)

Так .. - Він має непередбачувані результати, навіть якщо компілятор C # намагається якось його нормалізувати.

Я не знайшов жодної документації на C #, яка б описувала поведінку всіх вас, задокументованих як нормальну або чітко визначену поведінку мови. Що я знайшов - це навпаки!

[ скопійовано з документації MSDN для операторів збільшення та скорочення Postfix: ++ та - ]

Коли до аргументу функції застосовується оператор postfix, значення аргументу не гарантується збільшення або зменшення, перш ніж воно буде передане функції. Додаткову інформацію див. У розділі 1.9.17 стандарту C ++.

Зауважте, що ці слова не гарантовані ...

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


Я не впевнений, що я слідкую за 100%, але ви посилаєтесь на документацію C ++, але моє запитання стосувалося C #. Документація на це знаходиться тут .
Пітер

У своїй відповіді я мав на увазі С #. З наданого вами посилання: Результатом x ++ або x-- є значення x до початку операції, тоді як результат ++ x або --x - значення x після операції. В будь-якому випадку x сам має однакове значення після операції. чітко показує, що це не так при тестуванні .. тому що i=++iдасть різний результат від i=i++. Тому моя відповідь стоїть.
GY

Ага, гаразд, але це заплутано, коли ви посилаєтесь на документацію C ++. Отже, що ви говорите, це те, що специфікація не була реалізована правильно?
Пітер

Ні, що я говорю, це те, що це не визначено відповідно до специфікації, а використання невизначеного призведе до невизначених результатів.
GY

Не визначено в C ++, але C # каже, що після операції має бути те саме значення , ні? Це не те саме, що не визначено (але я згоден, ви не повинні його використовувати, дивіться мою відмову, я просто намагався зрозуміти, що відбувається).
Пітер

4

Оператор ++ після змінної робить приріст постфіксу. Збільшення відбувається після всього іншого у виписці, додавання та призначення. Якщо замість цього, ви поставите ++ перед змінною, це відбудеться до того, як буде оцінено значення i, і дасть очікувану відповідь.


2
++Чи не відбувається після+= того, як заяву, це відбувається під час виконання +=заяви. Ось чому наслідки ++переосмислення +=.
dtb

Використання ++ я фактично призводить до 1, а не до 2 (моя спочатку "очікувана відповідь").
Петро

Схоже, присвоєння += замінює модифікацію внаслідок висловлення до або після збільшення в виразі.
Стівен Лу

4

Етапи розрахунку:

  1. int i=0 // Ініціалізовано до 0
  2. i+=i++ // Рівняння
  3. i=i+i++ // після спрощення рівняння компілятором
  4. i=0+i++ // i значення підстановки
  5. i=0+0 // i ++ дорівнює 0, як пояснено нижче
  6. i=0 // Кінцевий результат i = 0

Тут спочатку значення iстановить 0. WKT - i++це не що інше, як: спочатку використовуйте iзначення, а потім збільшуйте iзначення на 1. Отже, воно використовує iзначення 0, обчислюючи, i++а потім збільшуючи його на 1. Отже, це призводить до значення з 0.


3

Є два варіанти:

Перший варіант: якщо компілятор прочитав твердження наступним чином,

i++;
i+=i;

то результат 2.

Для

else if
i+=0;
i++;

результат 1.


5
Жоден з них не є фактичним результатом.
Стівен Лу

3

Будьте дуже уважні: прочитайте відповіді на FAQ : те, що ви намагаєтеся зробити (змішування призначення та ++тієї самої змінної), є не лише визначеним, але й невизначеним (це означає, що компілятор може робити що завгодно при оцінці! "обґрунтовані" результати).

Прочитайте, розділ 3 . Весь розділ варто прочитати! Особливо 3.9, що пояснює наслідки не визначеного. У розділі 3.3 наведено короткий підсумок того, що ви можете, а чого не можете зробити за допомогою "i ++" тощо.

Залежно від внутрішніх компіляторів, ви можете отримати 0, або 2, або 1, або навіть що-небудь інше! І як це не визначено, для них це нормально.


oops, c # ... Мене скинули "gcc", дехто пройшов, щоб розібрати код.
Олів’є Дулак

1
Я пропустив, що це теж був C #, але відповідь все одно сподобалась.
Ієн Коллінз

1
@Iain: спасибі, я теж вважаю, що варто було тримати відповідь доступною, багато людей не знають про це (або про той чудовий фейк, з найкращого часу Usenet, коли більшість людей, які мають знання про субджет збиралися в той самий місце для його оновлення)
Олів'є Дулак

3

У наведених вище відповідях є багато відмінних міркувань, я просто зробив невеликий тест і хочу поділитися з вами

int i = 0;
i+ = i++;

Тут результат i показує 0 результат. Тепер розглянемо нижче випадки:

Випадок 1:

i = i++ + i; //Answer 1

Раніше я думав, що вище код схожий на це, тому на перший погляд відповідь - 1, а реальна відповідь i для цього - 1.

Випадок 2:

i = i + i++; //Answer 0 this resembles the question code.

тут оператор приросту не вступає в шлях виконання, на відміну від попереднього випадку, коли i ++ має шанс виконати перед додаванням.

Я сподіваюся, що це трохи допомагає. Дякую


2

Сподіваючись відповісти на це з точки зору програмування С типу 101.

Мені здається, що це відбувається в такому порядку:

  1. iоцінюється як 0, що призводить i = 0 + 0до того, що приріст операції i++"у черзі", але присвоєння 0 0 iще не відбулося.
  2. Приріст i++відбувається
  3. Призначення i = 0зверху відбувається, фактично перезаписуючи все, що було б зроблено №2 (післякріплення).

Тепер # 2 ніколи насправді не може статися (мабуть, ні?), Оскільки компілятор, ймовірно, усвідомлює, що він не буде служити меті, але це може залежати від компілятора. Так чи інакше, інші більш обізнані відповіді показали, що результат правильний і відповідає стандарту C #, але не визначено, що відбувається тут для C / C ++.

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

Крім того, ви не очікували, що результат буде 2 незалежно, якщо ви не зробили це, ++iа не i++я вірю.


1
Попередня версія призводить до результатів 2C ++: ideone.com/8dH8tf
Стівен Лу

Що має сенс. Але попередній приріст - це дещо менш складна ситуація, ніж після збільшення.
gkimsey

2

Простіше кажучи,

i ++, додасть 1 до "i" після завершення оператора "+ =".

Те, що ви хочете, є ++ i, так що воно додасть 1 до "i" до виконання оператора "+ =".


0
i=0

i+=i

i=i+1

i=0;

Потім додається 1 i.

i + = i ++

Отже, перш ніж додати 1 до i, iприйняв значення 0. Тільки якщо ми додамо 1 раніше, iотримаємо значення 0.

i+=++i

i=2

-4

Відповідь iбуде 1.

Давайте подивимось, як:

Спочатку i=0;.

Тоді під час обчислення i +=i++;відповідно до значення ми матимемо щось на кшталт 0 +=0++;, тож згідно з пріоритетом оператора 0+=0виступить першим і результат буде 0.

Тоді оператор приросту буде застосований як 0++, як 0+1і значення iбуде 1.


3
Ця відповідь неправильна. Ви не отримаєте 1, тому що, коли ви виконуєте 0 += 0++;завдання, це приріст, ++але зі значенням, яке я інтерпретується до ++(тому що це поштовий оператор.
PhoneixS

2
Вибачте, але це неправильно. Прочитайте моє запитання, і ви побачите, я кажу, що результат дорівнює 0. Якщо запустити код, ви побачите, що він дієвий 0.
Пітер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.