Для 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
Это работает, как и ожидалось
Стив - D

177
Сколько вариантов этого вопроса будет задано?
Mowwwalker

20
Предварительная инкрементация будет увеличивать значение перед выполнением действия i + = ++ 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 становится результатом операции.

Обратите внимание, что из-за порядка приоритета , постфикс ++происходит раньше += , но результат в итоге не используется (так как используется предыдущее значение 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
Первая часть ответа является излишней. Ваш последний пример кода мог бы сделать это ИМХО. +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 второй я зависит от прироста
    • Func(i++, i) второй я затронут
  • другие виды использования в той же строке относятся к оператору короткого замыкания, как ||и &&:
    • (false && i++ != i) || i == 0 третий я не затронут i ++, потому что он не оценивается

Так что есть смысл: i += i++;?

Это так же, как i = i + i++;

Порядок оценки:

  1. Хранить i + i (то есть 0 + 0)
  2. Инкремент i (i становится 1)
  3. Присвойте значение шага 1 i (i становится 0)

Не то, что приращение отбрасывается.

Что это значит: i = i++ + i;?

Это не то же самое, что в предыдущем примере. 3-й iвлияет на прирост.

Порядок оценки:

  1. Store i (то есть 0)
  2. Инкремент i (i становится 1)
  3. Сохраните значение шага 1 + i (то есть 0 + 1)
  4. Присвойте значение шага 3 i (i становится 1)

22
+ 1 ++ - для чистого хардкорного вскрытия. Чак Норрис был бы горд :) Я предполагаю, что вы действительно предполагаете, что операционная система установлена ​​на Intel, а не на моно-порт ...
StuartLC

19
C # имеет четко определенный порядок оценки для выражения, и объектный код просто реализует этот порядок. Вывод машинного кода не является причиной или объяснением порядка оценки.
Каз

8
Машинный код позволяет легко понять, как реализован порядок оценки IMO.
Кевин

5
@StuartLC Я вижу, что ты там сделал. Позор об этом отброшенном голосовании все же.
Штеффан Донал

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.
Jong

32

Это просто слева направо, восходящая оценка абстрактного синтаксического дерева. Концептуально, дерево выражения идет сверху вниз, но оценка разворачивается, когда рекурсия возвращается вверх по дереву снизу.

// source code
i += i++;

// abstract syntax tree

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

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

Правая сторона - постинкрементный ++оператор. Он имеет один операнд, iкоторый оценивается как источник значения и место хранения значения. Оператор оценивает i, находит 0и, следовательно, сохраняет 1в этом месте. Возвращает предыдущее значение,0 в соответствии со своей семантикой возврата предыдущего значения.

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

Как и Java, C # санировал очень оригинальный аспект языка 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++сначала возвращает значение, затем увеличивает его. Но после того, как я установлен в 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, ТО увеличьте его.

i + = i ++ означает: принять текущее значение i. Добавьте результат i ++.

Теперь давайте добавим в качестве начального условия i = 0. i + = i ++ теперь оценивается так:

  1. Какова текущая стоимость я? Это 0. Сохраните его, чтобы мы могли добавить к нему результат i ++.
  2. Оцените i ++ (оценивается как 0, потому что это текущее значение i)
  3. Загрузите сохраненное значение и добавьте к нему результат шага 2. (добавьте 0 к 0)

Примечание. В конце шага 2 значение i на самом деле равно 1. Однако на шаге 3 вы отбрасываете его, загружая значение i до его увеличения.

В отличие от i ++, ++ i возвращает увеличенное значение.

Поэтому я + = ++ я бы дал вам 1.


Это помощь Фулл
Сонша

11

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


8

i += i++; будет равен нулю, потому что это делает ++ потом.

i += ++i; сделаю это раньше


4
Если это произойдет ++потом, я ожидаю, что результат будет 1.
Comecme

8

Постфикс ++ обрабатывается iперед его увеличением и +=вычисляется только iодин раз.

Следовательно, 0 + 0 = 0, так как iоценивается и используется до его увеличения, поскольку используется формат постфикса ++. Чтобы iувеличить сначала, используйте префикс form ( ++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)))значения я равен 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++(постфикс) запоминает значение iбыло, и называет префикс нотацию ( ++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 .

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

Почему я так зол на вас, люди? не из-за чего ваши ответы объясняются ..
Я имею в виду, что каждый прочитанный мною ответ прилагал значительные усилия, чтобы объяснить невозможное, я аплодирую!

Но каков результат? это интуитивный результат - это приемлемый результат?

Каждый из вас видел «голого короля» и каким-то образом принял его за рационального короля.

Вы все НЕПРАВИЛЬНО!

i+=i++;результат 0не определен.

ошибка в механизме оценки языка, если хотите .. или даже хуже! ошибка в дизайне.

хотите доказательства? конечно хочешь!

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

Теперь это ... интуитивный результат! потому что мы сначала оценили tприсвоенное ему значение и только после оценки и присвоения у нас произошла операция post - не рационально ли?

это рационально, что: i=i++и i=iдать тот же результат для i?

в то время как t=i++и t=iимеют разные результаты для i.

Операция post - это то, что должно произойти после оценки оператора.
Следовательно:

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 и многие другие источники говорят нам: «Эй, это не определено!»

Теперь, прежде чем я продолжу, даже этот набор примеров, которые я привел, никем не поддерживается и не признается. Однако именно так, согласно интуитивному и рациональному подходу, должен был быть результат.

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

Если он написан так, что не будет соответствовать определениям языка - это ошибка!

И в завершение я скопировал это из Википедии, операторов увеличения и уменьшения :
поскольку оператор увеличения / уменьшения изменяет свой операнд, использование такого операнда более одного раза в одном выражении может привести к неопределенным результатам . Например, в выражениях типа x - ++ x не ясно, в какой последовательности должны выполняться операторы вычитания и приращения. Подобные ситуации усугубляются, когда компилятор применяет оптимизации, что может привести к тому, что порядок выполнения операций будет отличаться от того, который предполагал программист.

И поэтому.

Правильный ответ: это НЕ ДОЛЖНО ИСПОЛЬЗОВАТЬСЯ! (как это НЕ УКАЗАНО!)

Да .. - Это имеет непредсказуемые результаты, даже если C # complier пытается как-то его нормализовать.

Я не нашел никакой документации по C #, описывающей поведение, которое вы все описали как нормальное или четко определенное поведение языка. То, что я нашел, - полная противоположность!

[ скопировано из документации MSDN для операторов приращения и убывания Postfix: ++ и - ]

Когда к аргументу функции применяется постфиксный оператор, значение аргумента не обязательно увеличивается или уменьшается до его передачи в функцию. См. Раздел 1.9.17 в стандарте C ++ для получения дополнительной информации.

Обратите внимание, эти слова не гарантированы ...

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


Я не уверен, что следую 100%, но вы ссылаетесь на документацию C ++, но мой вопрос был о C #. Документация на это здесь .
Питер

Я имел в виду C # в своем ответе. По предоставленной вами ссылке: Результатом x ++ или x-- является значение x перед операцией, тогда как результатом ++ x или --x является значение x после операции. В любом случае, x сам имеет то же значение после операции. ясно показывает, что это не тот случай, когда тестирование .. потому что i=++iбудет предоставлять другой результат из i=i++. Поэтому мой ответ остается в силе.
GY

Ага, хорошо, но это сбивает с толку, когда вы ссылаетесь на документацию C ++. Так что вы говорите, что спецификация не была правильно реализована?
Питер

Нет. То, что я говорю, это то, что он не определен в соответствии со спецификацией, и использование undefined приведет к неопределенным результатам.
GY

Не определено в C ++, но C # говорит, что после операции должно быть то же значение , нет? Это не то же самое, что undefined (но я согласен, что вы не должны его использовать, см. Мой отказ от ответственности, я просто пытался понять, что происходит).
Питер

4

Оператор ++ после переменной делает это постфиксным приращением. Инкремент происходит после всего остального в операторе, добавления и присваивания. Если вместо этого вы поставите ++ перед переменной, это произойдет до того, как будет вычислено значение i, и даст вам ожидаемый ответ.


2
++Не происходит после+= того, как заявление, это происходит во время выполнения +=заявления. Вот почему эффекты ++переопределить с помощью +=.
ДТБ

Используя ++, я фактически получаю 1, а не 2 (мой изначально «ожидаемый ответ»).
Питер

Похоже, что назначение += перезаписывает модификацию из-за пре- или постинкрементного выражения.
Стивен Лу

4

Шаги в расчете:

  1. int i=0 // Инициализирован до 0
  2. i+=i++ //Уравнение
  3. i=i+i++ // после упрощения уравнения компилятором
  4. i=0+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 по C : то, что вы пытаетесь сделать (смешивая присваивание и одну и ++ту же переменную), не только не определено, но и не определено (это означает, что компилятор может делать что угодно при оценке !, не только давая «обоснованные» результаты).

Пожалуйста, прочитайте раздел 3 . Весь раздел стоит прочитать! Особенно 3.9, что объясняет неуказанное значение. Раздел 3.3 дает вам краткое изложение того, что вы можете и не можете делать с «i ++» и тому подобным.

В зависимости от внутренних компонентов компилятора, вы можете получить 0, или 2, или 1, или даже что-нибудь еще! И поскольку он не определен, для них это нормально.


К сожалению, C # ... Я был сбит с толку "GCC", некоторые прошли через разобрать код.
Оливье Дюлак

1
Я тоже скучал по C #, но в любом случае мне понравился ответ.
Иэн Коллинз

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

3

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

int i = 0;
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

Надеюсь ответить на это с точки зрения программирования на языке C.

Похоже, это происходит в следующем порядке:

  1. iоценивается как 0, в результате чего i = 0 + 0операция приращения i++"ставится в очередь", но присвоение 0 iеще не произошло.
  2. Приращение i++происходит
  3. Назначение i = 0сверху происходит, эффективно перезаписывая все, что сделал бы # 2 (постинкремент).

Теперь # 2 может никогда не произойти (вероятно, нет?), Потому что компилятор, вероятно, понимает, что это не будет служить цели, но это может зависеть от компилятора. В любом случае, другие, более осведомленные ответы показали, что результат верен и соответствует стандарту C #, но не определено, что происходит здесь для C / C ++.

Как и почему не в моей компетенции, но тот факт, что ранее оцененное правостороннее назначение происходит после постинкремента, вероятно, здесь сбивает с толку.

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


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

В этом есть смысл. Но прединкремент является несколько менее сложной ситуацией, чем постинкремент.
gkimsey

2

Проще говоря,

i ++, добавит 1 к «i» после завершения оператора «+ =».

Что вам нужно, так это ++ i, так что он добавит 1 к «i» перед выполнением оператора «+ =».


0
i=0

i+=i

i=i+1

i=0;

Затем 1 добавляется к 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++;присваивание после инкремента, ++но со значением i, интерпретируемым до ++(потому что является оператором почты.
PhoneixS

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