Является ли (--i == i++) неопределенным поведением?

этот вопрос связан с моей предыдущей проблемой. Я получил следующий ответ: "Это неопределенное поведение".

Пожалуйста, кто-нибудь объясните:

  • Что такое неопределенное поведение?
  • как я могу узнать, что мой код имеет неопределенное поведение?

Пример кода:

int i = 5;
if (--i == i++)         
   Console.WriteLine("equal and i=" + i);           
else
   Console.WriteLine("not equal and i=" + i);

//output: equal and i=6

person Javed Akram    schedule 14.03.2011    source источник
comment
С каким языком вы работаете? C# и C очень разные   -  person Cameron    schedule 14.03.2011
comment
А, С или С#? Это недопустимо C, почему вы добавили тег?   -  person GManNickG    schedule 14.03.2011
comment
Что такое неопределенное поведение? Его 3 или 428,3 в зависимости от гравитации и силовых установок рядом.   -  person stefan    schedule 14.03.2011
comment
зачем тебе вообще так писать, чтобы запутать своего коллегу-программиста?   -  person AndersK    schedule 14.03.2011
comment
Что Console.WriteLine() делает в C?   -  person Prasoon Saurav    schedule 14.03.2011
comment
Код явно C#, поэтому я снова добавляю этот тег.   -  person Matthew Flaschen    schedule 14.03.2011
comment
Также прочитайте эту тему (stackoverflow.com/questions/4176328/), если вы хотите узнать больше о Undefined Behavior и Sequence Points.   -  person Prasoon Saurav    schedule 14.03.2011
comment
@PrasoonSaurav: это для C++, к которому этот вопрос не относится.   -  person Ben Voigt    schedule 01.11.2014


Ответы (8)


Что такое неопределенное поведение?

Это просто любое поведение, которое специально не определено соответствующей спецификацией языка. Некоторые спецификации перечисляют определенные вещи как явно неопределенные, но на самом деле все, что не описано как определенное, не определено.

как я могу узнать, что мой код имеет неопределенное поведение?

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

Будьте осторожны!

person Carl Norum    schedule 14.03.2011

Он не определен в C, но хорошо определен в C#:

Из раздела спецификации С# (ECMA-334) "Приоритет и ассоциативность операторов" (§14.2.1):

  • За исключением операторов присваивания и оператора объединения с нулевым значением, все бинарные операторы являются левоассоциативными, что означает, что операции выполняются слева направо. [Пример: x + y + z оценивается как (x + y) + z. конец примера]

Таким образом, --i оценивается первым, изменяя i на 4 и оценивая до 4. Затем вычисляется i++, изменяя i на 5, но оценивая до 4.

person Matthew Flaschen    schedule 14.03.2011
comment
Откуда это заявление? С или С#? - person Jeremiah Willcock; 14.03.2011
comment
Это правильно, что это определено, но это не то, где и как это определено. Это не присваивание и не условная операция. - person Guffa; 14.03.2011
comment
проблема не в ассоциативности или приоритете, оба они хорошо определены и в c, и в c++. Проблема в том, что в c(++) при вызовах функций (или операторов) у вас нет гарантий относительно того, в каком порядке вычисляются операнды между точками последовательности. Поскольку у вас есть только один бинарный оператор, ассоциативность здесь даже не имеет значения. Важно то, что C# указывает, что все операнды оцениваются слева направо, включая побочные эффекты (например, операцию присваивания). Более актуальную информацию можно найти здесь blogs.msdn.microsoft.com/ oldnewthing/20070814-00/?p=25593 - person Ordoshsen; 21.10.2018

Да, это выражение также является неопределенным поведением (в C и C++). См. http://en.wikipedia.org/wiki/Sequence_point для получения дополнительной информации о правилах; вы также можете искать «точку последовательности» в более общем смысле (это набор правил, которые нарушает ваш код).

person Jeremiah Willcock    schedule 14.03.2011

(Это предполагает C или C++.)

Ответ Карла в целом точен.

В частности, проблема заключается в том, на что указал Иеремия: точки последовательности.

Чтобы уточнить, кусок кода (--i == ++i) является одним "происшествием". Это кусок кода, который оценивается сразу. Нет определенного порядка того, что происходит первым. Сначала может быть оценена левая сторона, или правая сторона, или, может быть, сравнивается равенство, затем i увеличивается, а затем уменьшается. Каждое из этих действий может привести к тому, что это выражение будет иметь разные результаты. Это «неопределенно», что здесь произойдет. Вы не знаете, каким будет ответ.

Сравните это с утверждением i = i+1; Здесь правая сторона всегда оценивается первой, а затем ее результат сохраняется в i. Это хорошо определено. Нет никакой двусмысленности.

Надеюсь, это немного поможет.

person usul    schedule 14.03.2011

В C результат не определен, в C# он определен.

В C сравнение интерпретируется как:

Сделайте все это в любом порядке:
- Уменьшите i, затем получите значение i в x
- Получите значение i в y, затем увеличьте i
Затем сравните x и y.

В C# границ операций больше, поэтому сравнение интерпретируется как:

Уменьшите i
, затем получите значение i в x
, затем получите значение i в y
, затем увеличьте i
, затем сравните x и y.

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

person Guffa    schedule 14.03.2011

Потому что так указано в стандарте C. И ваш пример ясно показывает неопределенное поведение.

В зависимости от порядка оценки сравнение должно быть 4 == 5 или 5 == 6. И все же условие возвращает True.

person vz0    schedule 14.03.2011

Ваш предыдущий вопрос был помечен [C], поэтому я отвечаю на основе C, хотя код в вашем текущем вопросе не похож на C.

Определение неопределенного поведения в C99 гласит (§3.4.3):
1 неопределенное поведение
поведение при использовании непереносимой или ошибочной программной конструкции или ошибочных данных, для которых настоящий международный стандарт не предъявляет требований.

2 ПРИМЕЧАНИЕ Возможное неопределенное поведение варьируется от полного игнорирования ситуации с непредсказуемыми результатами до поведения во время трансляции или выполнения программы документированным образом, характерным для среды (с выдачей или без выдачи диагностического сообщения), до прекращения трансляции или выполнения (с выдача диагностического сообщения).

Приложение J.2 стандарта C содержит (длинный — несколько страниц) список неопределенного поведения, хотя даже он не является исчерпывающим. По большей части неопределенное поведение означает, что вы нарушили правила, поэтому способ узнать об этом — узнать правила.

person Jerry Coffin    schedule 14.03.2011

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

В вашем коде, поскольку он использует оператор сравнения равенства, который не указывает, какая сторона операндов должна выполняться первой, --i или i++ может в конечном итоге выполняться первой, и ваш ответ будет зависеть от фактического реализация компилятора. Если --i выполняется первым, это будет 4 == 4, i=5; если i++ будет реализовано первым, это будет 5 == 5, i=5.

Тот факт, что ответ может оказаться таким же, не мешает компилятору предупредить вас о том, что это неопределенная операция.

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

person Stephen Chung    schedule 14.03.2011