Подтвердить что ты не робот

Поведение операторов arr [i] = я ++ и я = я + 1 в C и С++

В языках C и С++ оператор arr[i] = i++; вызывает поведение undefined. Почему оператор i = i + 1; не вызывает поведение undefined?

4b9b3361

Ответ 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 Выражения

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

В 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, где все может случиться, С++ определяет, что может произойти только одна из двух вещей. Очевидно, что одна вещь слишком много, чтобы быть приемлемой в вашем коде.