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

Точки последовательности и частичный порядок

Несколько дней назад здесь обсуждалось о том, будет ли выражение

i = ++ я + 1

вызывает UB (Undefined Поведение) или нет.

Наконец, был сделан вывод о том, что он вызывает UB, поскольку значение "i" меняется более одного раза между двумя точками последовательности.

Я участвовал в обсуждении с Йоханнесом Шаубом в той же теме. По его словам,

i = (i, я ++, i) +1 ------ (1)/* также вызывает UB */

Я сказал, что (1) не вызывает UB, потому что побочные эффекты предыдущих подвыражений очищаются оператором запятой ',' между я и я ++ и между я ++ и i.

Затем он дал следующее объяснение:

"Да, точка последовательности после я ++ завершает все побочные эффекты до этого, но нет ничего, что останавливает побочный эффект присваивания с побочным эффектом я ++. Основная проблема заключается в том, что побочный эффект присваивания не указано, что произойдет после или до оценки обоих операндов задания, и поэтому точки последовательности не могут ничего сделать для защиты этого: точки последовательности вызывают частичный порядок: просто потому, что есть точка последовательности после и до того, как я ++ не означает, что все побочные эффекты секвенированы относительно i.

Также обратите внимание, что просто точка последовательности ничего не означает: порядок оценок не продиктован формой кода. Это диктуется семантическими правилами. В этом случае не существует семантического правила, говорящего, когда возникает побочный эффект присваивания в отношении оценки обоих его операндов или подвыражений этих операндов".

Заявление, написанное "смелым", смутило меня. Насколько я знаю:

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

Так как, операторы запятой также указывают порядок выполнения, побочный эффект я ++ был отменен, когда мы достигнем последнего i.He(Johannes), было бы правильно, если бы порядок оценки не был указан (но в случае запятой, это хорошо указано).

Итак, я просто хочу знать, вызывает ли (1) UB или нет?. Может ли кто-нибудь дать другое действительное объяснение?

Спасибо!

4b9b3361

Ответ 1

В стандарте C говорится об операторах присваивания (C90 6.3.16 или C99 6.5.16 Операторы присваивания):

Побочный эффект обновления сохраненного значения левого операнда должен происходить между предыдущей и следующей точкой последовательности.

Мне кажется, что в заявлении:

i=(i,i++,i)+1;

точка последовательности "предыдущая" к оператору присваивания будет вторым оператором запятой, а "следующей" точкой последовательности будет конец выражения. Поэтому я бы сказал, что выражение не вызывает поведение undefined.

Однако это выражение:

*(some_ptr + i) = (i,i++,i)+1;

будет иметь поведение undefined, потому что порядок оценки двух операндов оператора присваивания равен undefined, и в этом случае вместо проблемы, когда возникает побочный эффект оператора присваивания, проблема заключается в том, что вы не Не знаю, будет ли значение i, используемое в левом дескрипторе, оцениваться до или после правой стороны. Этот порядок оценки не встречается в первом примере, потому что в этом выражении значение i фактически не используется в левой части - все, что интересует оператор присваивания, это "lvalue-ness", i.

Но я также думаю, что все это достаточно отрывочно (и мое понимание связанных с ним нюансов достаточно отрывочно), что я не удивлюсь, если кто-то сможет убедить меня в другом (по любому поводу).

Ответ 2

Я считаю, что следующее выражение определенно имеет поведение undefined.

i + ((i, i++, i) + 1)

Причина в том, что оператор запятой указывает точки последовательности между подвыражениями в круглых скобках, но не указывает, где в этой последовательности происходит оценка левого операнда +. Между точками последовательности, окружающими i++, существует одна возможность, и это нарушает 5/4, поскольку i записывается между двумя точками последовательности, но также считывается дважды между одними и теми же точками последовательности, а не только для определения значения, которое нужно сохранить, но также для определения значения первого операнда для оператора +.

Это также имеет поведение undefined.

i += (i, i++, i) + 1;

Теперь я не уверен в этом утверждении.

i = (i, i++, i) + 1;

Хотя одни и те же принципы применяются, i должен быть "оценен" как модифицируемое значение lvalue и может быть сделано в любое время, но я не уверен, что его значение когда-либо читается как часть этого. (Или существует другое ограничение, которое выражение нарушает, чтобы вызвать UB?)

Подвыражение (i, i++, i) происходит как часть определения значения, которое нужно сохранить, и что подвыражение содержит точку последовательности после хранения значения до i. Я не вижу никакого способа, чтобы это не требовало, чтобы побочный эффект i++ был завершен до определения значения, которое нужно сохранить, и, следовательно, как можно скорее укажите, что может возникнуть побочный эффект присваивания.

После того, как значение этой точки i будет считано не более одного раза, и только для определения значения, которое будет сохранено на i, так что эта последняя часть в порядке.

Ответ 3

i=(i,i++,i)+1 ------ (1) /* invokes UB as well */

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

Хороший язык судоку.: -)

edit: Там более подробное объяснение здесь.

Ответ 4

Я был в замешательстве в начале относительно заявления Иоганнеса (litb), но он упомянул, что в:

i = (i, ++i, i) +1

<Johannes>
Если <a> является присвоением и является приращением. :s: - точка последовательности, тогда побочные эффекты могут быть секвенированы следующим образом между точками последовательности: (i :s: i++< a ><n> :s: i) + 1. Значение скаляра i было изменено дважды между первой и второй точками последовательности здесь. Порядок, в котором происходит присваивание и приращение, не указывается, и поскольку между ними нет точки последовательности, оно не является даже атомарным относительно друг друга. Это один разрешенный порядок, разрешенный неуказанным упорядочением этих побочных эффектов.

Это отличается от (i++, i++), потому что порядок оценки двух подвыражений слева направо, а в точке последовательности между ними приращение предыдущей оценки должно быть полным, а следующий приращение не должен иметь еще не произошло. Это гарантирует, что нет значения значения i между двумя точками последовательности, что делает (i++, i++) действительным </Йоханнеs >

Это заставило меня подумать, что последовательность, указанная в litb, недействительна, потому что согласно C99:

6.5.16.1 (2) В простом присваивании (=) значение правильного операнда преобразуется в тип выражения присваивания и заменяет значение, хранящееся в объекте, обозначенном левым операндом.

то есть. значение правильного операнда должно быть известно до побочного эффекта присваивания (изменение значения, сохраненного в объекте, соответствующем левому операнду)

6.5.17 (2) Левый операнд оператора запятой оценивается как выражение void; после его оценки появляется точка последовательности. Затем оценивается правый операнд; результат имеет свой тип и значение.

то есть. самый правый операнд операции с запятой нужно оценить, чтобы узнать значение и тип выражения для запятой (и значение правильного операнда для моего примера).

Итак, в этом случае "предыдущая точка последовательности" для побочного эффекта присваивания фактически была бы самой правой запятой. Возможная последовательность, упомянутая Йоханнесом, недействительна.

Пожалуйста, поправьте меня, если я ошибаюсь.