Поведение выражения: Определенный или Неопределенный?

У меня есть следующий код

int m[4]={1,2,3,4}, *y; 
y=m; 
*y = f(y++); // Expression A

Мой друг сказал мне это Expression A имеет четко определенное поведение, но я не уверен, корректен ли он.

Согласно ему функция f() представляет a sequence point промежуточный и следовательно поведение четко определено.

Кто-то разъяснитесь.

P.S.: Я знаю, что мы не должны писать такой код для практической цели. Это только в целях изучения.:)

9
задан Deduplicator 3 July 2015 в 15:43
поделиться

4 ответа

В лучшем случае рассматриваемый код имеет неопределенное поведение. Для операторов присваивания «порядок вычисления операндов не указан» (C99 §6.5.16 / 4).

Если левый операнд оценивается первым, результат f (y ++) будет сохранен в m [0] . Если первым вычисляется правый операнд, результат будет сохранен в m [1] .

Что касается того, является ли поведение неопределенным, соответствующий параграф:

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

Если сначала вычисляется левая часть, то мы сталкиваемся со вторым предложением, потому что порядок следующий:

  1. Значение y читается слева, чтобы разыменовать его
  2. значение y читается с правой стороны, чтобы увеличить его
  3. После оценки аргументов функции есть точка последовательности (поэтому побочный эффект y ++ Complete и y записывается в)

На шаге 1 считывается «предыдущее значение» y , но с целью, отличной от «определения значения для сохранения». Таким образом, поведение действительно не определено, потому что один действительный порядок оценки дает неопределенное поведение.

15
ответ дан 4 December 2019 в 07:34
поделиться

Выражение не определено должным образом:

Допустимая интерпретация выражения:

(1) int* t0 = y++; 
(2) int  t1 = f(t0);
(3) int& t2 = *y;
-----------------
t2 = t1; 

В равной степени допустимая интерпретация выражения:

(1) int& t2 = *y;
(2) int* t0 = y++; 
(3) int  t1 = f(t0);
-----------------
t2 = t1; 

Оба они являются действительными и генерируют разные результаты. Таким образом, выражение имеет неопределенный результат.

1
ответ дан 4 December 2019 в 07:34
поделиться

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

Сначала рассмотрим этот простой пример

i = some_function(i++);

Верно ли это? Да, это. Почему? Это верно, потому что точка последовательности, введенная функцией (та, о которой вы говорите) отделяет две модификации i друг от друга, тем самым делая код действительным. Нет порядка оценки этого выражения, который привел бы к двойному изменению i без промежуточной точки последовательности.

Однако вернемся к вашему варианту

*y = f(y++);

В этом случае точка последовательности тоже существует. Однако язык не дает никаких гарантий относительно порядка вычисления оператора = (что означает: язык не дает никаких гарантий относительно того, какой операнд оператора присваивания оценивается первым: левый или правый). Компилятор может сначала вычислить левую часть ( * y ), затем аргумент функции ( y ++ ), затем вызвать функцию и затем выполнить фактическое присвоение. . В этом потенциальном сценарии первые два шага - чтение y и изменение y - не разделены точкой последовательности. Таким образом, поведение не определено.

13
ответ дан 4 December 2019 в 07:34
поделиться

РЕДАКТИРОВАТЬ: это неверно, однако я оставляю его здесь, потому что обсуждение, которое следует в комментариях, несколько проясняет и, надеюсь, ценно.

Он четко определен на основе порядка вычисления операторов в C (или C ++).

Присваивание вынуждает сначала вычислить правую часть выражения. Приложение функции сначала принудительно оценивает свои аргументы, поэтому эффект кажется достаточно ясным (хотя я не пробовал запускать его, так что не стесняйтесь поправлять меня!). Мы можем переписать это, используя временные переменные (я назову их t0 и t1), и я считаю, что это могло бы быть немного яснее:

t0 = y++;
t1 = f(t0);
*y = t1;

Термин «точка последовательности» - это немного отвлекающий маневр. Точка последовательности на самом деле не создается, это просто следствие строгого порядка оценки, определенного для языка.

РЕДАКТИРОВАТЬ: хотя этот ответ кажется интеллектуально удовлетворительным, в ответе Джеймса Макнеллиса цитируется соответствующая часть спецификации C99, в которой говорится, что порядок оценки не четко определен. Полная заслуга ему за то, что он на самом деле проверил свои факты. Я собираюсь изменить свой ответ с «он четко определен» на «он, вероятно, четко определен для конкретного компилятора», поскольку я думаю, что маловероятно, что большинство компиляторов будут регулярно менять порядок, в котором они генерируют такой код ( Я говорю «вероятно», чтобы учесть любую очень агрессивную оптимизацию).

0
ответ дан 4 December 2019 в 07:34
поделиться