я = ++i + ++i; в С++

Может кто-нибудь объяснить мне, почему этот код печатает 14? Меня просто спросил другой ученик и не мог понять.

int i = 5;
i = ++i + ++i;
cout<<i;

Ответы

Ответ 1

Порядок побочных эффектов undefined в С++. Кроме того, изменение переменной дважды в одном выражении не имеет определенного поведения (см. С++ standard, §5.0.4, физическая страница 87/логическая стр. 73).

Решение. Не используйте побочные эффекты в сложном выражении, не используйте более одного в простых. И не мешает включить все предупреждения, которые может дать вам компилятор: добавление -Wall (gcc) или /Wall /W4 (Visual С++) в командную строку дает соответствующее предупреждение:

test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined

Очевидно, код компилируется в:

i = i + 1;
i = i + 1;
i = i + i;

Ответ 2

Это поведение undefined, результат будет зависеть от используемого вами компилятора. См. Например, С++ FAQ Lite.

Ответ 3

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

Указанные биты стандарта исходят из текущего стандарта С++ (ISO/IEC 14882: 2003). Там такая же формулировка в стандарте C.

Согласно стандарту С++, изменение значения более одного раза в наборе точек последовательности приводит к поведению undefined (раздел 5, пункт 4):

За исключением тех случаев, когда отмечено, порядок оценка операндов отдельных операторов и подвыражений отдельные выражения и порядок в которых происходят побочные эффекты, неуказан .53) Между предыдущими и следующая точка последовательности - скаляр объект должен иметь сохраненное значение не более чем один раз оценка выражения. Кроме того, предыдущее значение должно быть доступ только для определения значения для хранения. Требования этого пункт должен быть соблюден для каждого допустимый порядок подвыражения полного выражения; в противном случае поведение undefined. [Пример:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented

-end пример]

Обратите внимание, что второй пример "i = 7, i++, i++;" определяется, поскольку оператор запятой является точкой последовательности.

Здесь, что говорит стандарт С++: "undefined поведение" означает:

1.3.12 undefined поведение [defns.undefined]

например, может возникнуть при использовании ошибочной конструкции программы или ошибочных данных, для которой это Международный стандарт не предъявляет никаких требований. undefined также можно ожидать, когда это В Международном стандарте отсутствует описание любого явного определения поведения. [Примечание: допустимо undefinedповедение варьируется от полного игнорирования ситуации с непредсказуемыми результатами, поведения во время перевод или выполнение программы в документально оформленной форме, характерной для окружающей среды (с или без выдача диагностического сообщения), до прекращения перевода или исполнения (с выдачей диагностическое сообщение). Многие ошибочные программные конструкции не порождают поведение undefined; они должны быть диагностированы. ]

Другими словами, компилятор может делать все, что захочет, включая

  • выплевывает сообщение об ошибке,
  • выполнение определенной реализации и документирование,
  • с абсолютно непредсказуемыми результатами

Второй элемент охватывает языковые расширения, которые большинство компиляторов имеют, но, конечно, не определены в стандарте.

Поэтому я предполагаю, что строго говоря, что-то, что демонстрирует поведение undefined, не является "незаконным", но в моем опыте всякий раз, когда что-то было в программе на C/С++, которая демонстрирует поведение undefined (если это не расширение) - Это ошибка. Я думаю, что называть такую ​​конструкцию незаконной не путать, вводить в заблуждение или ошибочно.

Кроме того, я думаю, что попытка объяснить, что делает компилятор для достижения значения 14, не особенно полезна, поскольку это не соответствует точке. Компилятор может делать почти все; на самом деле, вероятно, что компилятор может достичь другого результата при запуске с использованием различных опций оптимизации (или может привести к сбою кода - кто знает?).

Для тех, кто хочет получить дополнительные ссылки или призыв к власти, вот несколько указателей:

Стив Саммит (сторонник часто задаваемых вопросов по comp.lang.c) длинный, длинный ответ на эту тему с 1995 года:

Вот что Бьярн Страуструп должен сказать по этому поводу:


Сноска: стандарт С++ использует слово "незаконный" ровно один раз - при описании разницы между С++ и стандартом C относительно использования static или extern с объявлениями типа.

Ответ 4

Простой... ваш компилятор оценивает ОБА приращения перед выполнением суммы без кэширования промежуточных результатов. Это означает, что когда вы добавляете я дважды, теперь оно имеет значение 7.

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

int j=++i; 
int k=++i;

i = j+k;

вы увидите 13, как ожидалось.

Ответ 5

В вашем конкретном компиляторе он сначала выбирает обе операции ++, затем добавляет. Он интерпретирует код как:

int i = 5;
++i;
++i;
i = i + i;
cout << i;

Это совершенно верно.

Ответ 6

Лучший вопрос: всегда ли это будет 14?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

По всей вероятности, он, вероятно, будет 14, потому что он имеет несколько больше смысла.

Ответ 7

Я думаю, что при рассмотрении проблемы с точки зрения дерева синтаксиса ответ на проблему становится яснее:

я
|
=
|
+
|
унарное выражение - унарное выражение

унарное выражение: унарное операторное выражение

В нашем случае выражение сводится к переменной i.

Теперь случается, что оба унарного выражения изменяют один и тот же операнд, поэтому код выполняет два раза ++ я при оценке унарных выражений перед добавлением результатов обоих унарных выражений.
Итак, что делает код действительно
++ я;
++ я;
i = я + i;

Для я = 5, что означает
i = я + 1;//i < - 6
i = я + 1;//i < - 7
i = я + i;//i < - 14

Ответ 8

Поскольку приоритет префикса имеет приоритет:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14

Ответ 9

 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14