c - Почему я = ++ я вызываю неопределенное поведение?
Я понимаю, что C использует понятие точек последовательности для определения неоднозначных вычислений, и что =
Оператор не является точкой последовательности. Однако я не вижу никакой двусмысленности в выполнении заявления
i = ++i
Насколько я понимаю, это просто равносильно оценке того, что в &i
, увеличивая его и сохраняя обратно в том же месте. Тем не менее, GCC помечает это как:
Операция [Warning] для 'i' может быть неопределенной [-Wsequence-point]
Я что-то упустил о том, как =
функции?
РЕДАКТИРОВАТЬ: Прежде чем пометить как дубликат, обратите внимание, что я просмотрел другие сообщения о точках последовательности и неопределенного поведения. Ни один из них не обращается к выражению i=++i
(обратите внимание на предварительный инкремент) конкретно. Упомянутые выражения, как правило, i=i++
, a=b++ + ++b
и т.д. И я не сомневаюсь ни в одном из них.
2 ответа
Вы упускаете что-то из неопределенного поведения. Неопределенное поведение просто означает, что компилятор может делать все, что захочет. Он может выдать ошибку, он может (как это делает GCC) показать предупреждение, он может заставить демонов вылететь из вашего носа. Прежде всего, он не будет вести себя хорошо и не будет работать согласованно между компиляторами, так что не делайте этого!
В этом случае компилятор НЕ должен гарантировать, что побочные эффекты от lhs оператора должны быть завершены до того, как возвращается rhs оператора. Это кажется смешным для вас, но вы не думаете, как компьютер. Он может, если захочет, вычислить возвращаемое значение и вернуть его в регистр, присвоить его i, а затем выполнить приращение фактического значения. Так что это будет выглядеть как
register=i+1;
i=register;
i=i+1;
Стандарт не дает вам гарантии, что этого не произойдет, так что просто не делайте этого!
Неопределенное поведение возникает потому, что переменная i
изменяется более одного раза между двумя точками последовательности. Точки последовательности - это точки, после которых видны все побочные эффекты предыдущих оценок, но никаких будущих побочных эффектов не видно. Стандарт гласит:
Между предыдущей и следующей точкой последовательности объект должен иметь свое сохраненное значение, измененное не более одного раза путем оценки выражения. Кроме того, предыдущее значение должно быть только для чтения, чтобы определить значение, которое будет сохранено.
Итак, какие побочные эффекты нас беспокоят?
++i
, который присваивает мне значениеi+1
i = ++i
, который присваивает i значение выражения++i
, которыйi+1
Итак, мы собираемся получить два (по общему признанию, эквивалентных) побочных эффекта: назначение i+1
к переменной i
, Что нас беспокоит, так это то, между какими двумя точками последовательности возникают эти побочные эффекты?
Какие операции составляют точки последовательности? Есть несколько, но есть только один, который на самом деле актуален здесь:
- в конце полного выражения (в этом случае
i = ++i
это полное выражение)
А именно, предварительное увеличение ++i
не точка последовательности. Это означает, что оба побочных эффекта (приращение и назначение) будут происходить между одними и теми же двумя точками последовательности, изменяя одну и ту же переменную i
, Таким образом, это неопределенное поведение; тот факт, что обе модификации имеют одинаковое значение, несущественен.
Но почему плохо изменять переменную несколько раз между точками последовательности? Чтобы предотвратить такие вещи, как:
i = ++i + 1;
Вот, i
увеличивается, но затем ему также присваивается значение (i+1) + 1
, из-за семантики предварительного приращения. Поскольку побочные эффекты имеют неоднозначный порядок, поведение не определено.
Теперь в стандарте может быть гипотетически сделан особый случай, что множественная модификация между двумя точками последовательности в порядке, если значения одинаковы, но это, вероятно, без необходимости усложнит реализацию компилятора без особой выгоды.