В языках C и С++ оператор arr[i] = i++;
вызывает поведение undefined. Почему оператор i = i + 1;
не вызывает поведение undefined?
Поведение операторов arr [i] = я ++ и я = я + 1 в C и С++
Ответ 1
Поскольку это изначально было помечено С++, а не любая конкретная версия (ы), ниже ответ является общим ответом на проблему. Тем не менее, обратите внимание на С++, C++17
и далее, поведение изменилось. Пожалуйста, см. Этот ответ Барри, чтобы узнать больше.
Для утверждения
arr[i] = i++;
значение i
используется как в операндах, RHS (справа), так и в LHS (слева), а в одном из случаев значение изменяется (как сторона эффект post ++
), где нет точки последовательности между ними, чтобы определить, какое значение i
следует учитывать. Вы также можете проверить этот канонический ответ.
С другой стороны, для i = i + 1
значение i
используется только в RHS, вычисленный результат сохраняется в LHS, другими словами, нет никакой двусмысленности. Мы можем написать то же утверждение, что и i++
, которое
- читает значение
i
- Увеличивает его на
1
- возвращает его обратно в
i
в четко определенной последовательности. Следовательно, никаких проблем.
Ответ 2
Обратите внимание, что это изменится на С++ 17. В С++ 17 arr[i] = i++
не вызывает поведение undefined. Это связано с следующим изменением [expr.ass]:
Во всех случаях назначение выполняется после вычисления значения правого и левого операндов и перед вычислением значения выражения присваивания. Правильный операнд упорядочен перед левым операндом.
То есть, мы делаем i++
, тогда мы выполняем arr[i]
, затем выполняем присвоение. Теперь четко определенное упорядочение:
auto src = i++;
auto& dst = arr[i];
dst = src;
Ответ 3
Для C99 мы имеем:
6.5 Выражения
- Между предыдущей и следующей точкой последовательности объект должен иметь сохраненное значение не более чем один раз, оценивая выражение. Кроме того, предыдущее значение должны быть прочитаны только для определения значения, которое необходимо сохранить.
В arr[i] = i++
значение i
изменяется только один раз. Но arr[i]
также читается из i
, и это значение не используется для определения нового значения i
. Вот почему он имеет поведение undefined.
С другой стороны, в i = i + 1
мы читаем i
, чтобы вычислить i + 1
, который используется как новое значение i
. Поэтому это выражение прекрасно.
Ответ 4
arr[i] = i++;
следует, что
- выражение правой руки оценивается перед назначением Оператор
- оценивается перед назначением
но содержит неоднозначность относительно порядка оценки выражения правой руки и оценки оператора индекса, компилятор может рассматривать его как
auto & val{arr[i]};
i++;
auto const rval{i};
val = rval;
или
i++;
auto & val{arr[i]};
auto const rval{i};
val = rval;
или как (тот же результат, что и выше)
i++;
auto const rval{i};
auto & val{arr[i]};
val = rval;
Это может привести к непредсказуемому результату, а
i = i + 1;
dos не имеет никакой двусмысленности, выражение правой руки оценивается перед назначением:
auto const rval{i + 1};
auto & val{i};
val = rval;
или (тот же результат, что и выше)
auto & val{i};
auto const rval{i + 1};
val = rval;
Ответ 5
В вашем примере a [i] = я ++, если я = 3, например, вы считаете, что сначала оценивается [i], или я ++? В одном случае значение 3 будет сохранено в [3], в другом случае оно будет сохранено в [4]. Очевидно, что у нас есть проблема. Ни один здравомыслящий человек не посмеет написать этот код, если не найдет гарантии, что именно произойдет здесь. (Java дает эту гарантию).
Как вы думаете, может быть проблема с я = я + 1? Язык должен сначала прочитать i, чтобы вычислить я + 1, а затем сохранить этот результат. Здесь нет ничего плохого. То же самое с [i] = я + 1. Оценка я + 1, в отличие от я ++, не меняет i. Поэтому, если я = 3, номер 4 должен быть сохранен в [3].
Различные языки имеют различные правила для исправления проблемы с помощью [i] = я ++. Java определяет, что происходит: выражения оцениваются слева направо, включая их побочные эффекты. C определяет его как поведение undefined. С++ не делает его undefined, а просто неопределенным. В нем говорится, что сначала оценивается либо [i], либо я ++, а другой - следующий, но он не говорит, какой из них. Таким образом, в отличие от C, где все может случиться, С++ определяет, что может произойти только одна из двух вещей. Очевидно, что одна вещь слишком много, чтобы быть приемлемой в вашем коде.