Имеет ли 'a [i] = i;' всегда приводят к хорошо определенному поведению? - программирование
Подтвердить что ты не робот

Имеет ли 'a [i] = i;' всегда приводят к хорошо определенному поведению?

Есть несколько интересных вопросов, поднятых здесь относительно поведения undefined в C. Один из них (слегка изменен)

Является ли следующий фрагмент кода результатом undefined?

int i = 0, *a = &i;   // Line 1
a[i] = i + 1;         // Line 2

Так как нет конкретного ответа на эту часть вопроса, и я заинтересован в том, чтобы знать поведение в С++, я поднимаю его снова здесь.


Правило № 2 из Undefined Поведение и точки последовательности говорит

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

Очевидно, что в приведенном выше примере дважды обращается к значению: a[i] (lhs) и i (rhs), и только один из них (rhs) определяет значение, которое нужно сохранить.

Ли строка 2 нарушает правило выше и приводит к поведению undefined в С++ 03?


Существует некоторая путаница в отношении того, изменяется ли i в строке 2?

Да, это изменено!

4b9b3361

Ответ 1

Это приведет к поведению undefined в С++ 03 и корректному поведению в С++ 11.

С++ 03: undefined Behvaior

Из стандарта С++ 03, раздел 5, пункт 4:

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

Обратите внимание на второе предложение: предыдущее значение i может использоваться только для определения значения, которое необходимо сохранить. Но здесь он также используется для определения индекса массива. Так как это назначение изменит i, a[0] = i+1 будет корректно определено, а a[i] = i+1 - нет. Обратите внимание, что присваивание не создает точку последовательности: только конец полного выражения (точка с запятой) делает.


С++ 11: Хорошо определенное поведение:

С++ 11 избавился от понятия точек последовательности и вместо этого определил, какие оценки секвенированы до того.

Из стандарта, раздел 1.9, пункт 15:

Вычисления значений операндов оператора секвенируются перед вычислением значения результата оператора. Если побочный эффект скалярного объекта не зависит от другого побочного эффекта на одном и том же скалярном объекте или вычисления значения с использованием значения одного и того же скалярного объекта, поведение undefined.

Оба операнда оператора присваивания секвенированы перед фактическим присваиванием. Таким образом, будут оцениваться как a[i], так и i+1, и только после этого будет изменен i. Результат хорошо определен.

Ответ 2

int i = 0, *a = &i;

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

a[i] = i;

i никоим образом не изменяется, поэтому здесь нет UB.

Ответ 3

Разложим ли выражение a[i] = i + 1?

= -- [] -- a
  \     \_ i
   \
    \_ + -- i
         \_ 1

Фактически, a[i] относится к &i, однако обратите внимание, что ни a[i], ни i+1 не изменяет i. i изменяется только тогда, когда выполняется = (само присваивание).

Так как операнды любой функции нужно оценить до того, как эта функция вступит в силу, это фактически эквивалентно:

void assign(int& address, int value) { address = value; }

assign(a[i], i + 1);

Верно, что = несколько отличается тем, что он встроен и не приводит к вызову функции, но оценка обоих операндов упорядочена до фактического назначения, поэтому они сначала оцениваются до i, а a[i] (который указывает на местоположение i) присваивается.

Ответ 4

Undefined поведение в этом случае произойдет только в том случае, если вы измените тот же адрес памяти без точки последовательности между изменениями. В частности, спецификация C99, раздел 6.5/2,

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

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

Ответ 5

Я хотел бы отметить одно: a[i] = i не всегда ведет к четко определенному поведению. Причина, по которой поведение четко определено в указанном случае, обусловлено начальными значениями i и a.

Позвольте мне уточнить:

int i = 1, *a = &i;   // Line 1, i initialized to anything other than 0
a[i] = i + 1;         // Line 2, all of a sudden we are in buffer over/underflow

Для любого другого начального значения i мы получаем доступ к другой ячейке памяти из самой i, которая создает поведение undefined.

Ответ 6

Нет, нет. Первая строка имеет точку последовательности (запятую), поэтому это не поведение undefined:

int i = 0, *a = &i;

Вторая строка совершенно нормальная.

a[i] = i + 1;

Так как i + 1 создает временное значение, i изменяется только один раз, в присваивании. Однако это будет undefined поведение:

a[i] = i++;