Точки последовательности и частичный порядок
Несколько дней назад здесь обсуждалось о том, будет ли выражение
i = ++ я + 1
вызывает UB
(Undefined Поведение) или нет.
Наконец, был сделан вывод о том, что он вызывает UB, поскольку значение "i" меняется более одного раза между двумя точками последовательности.
Я участвовал в обсуждении с Йоханнесом Шаубом в той же теме. По его словам,
i = (i, я ++, i) +1 ------ (1)/* также вызывает UB */
Я сказал, что (1) не вызывает UB, потому что побочные эффекты предыдущих подвыражений очищаются оператором запятой ',' между я и я ++ и между я ++ и i.
Затем он дал следующее объяснение:
"Да, точка последовательности после я ++ завершает все побочные эффекты до этого, но нет ничего, что останавливает побочный эффект присваивания с побочным эффектом я ++. Основная проблема заключается в том, что побочный эффект присваивания не указано, что произойдет после или до оценки обоих операндов задания, и поэтому точки последовательности не могут ничего сделать для защиты этого: точки последовательности вызывают частичный порядок: просто потому, что есть точка последовательности после и до того, как я ++ не означает, что все побочные эффекты секвенированы относительно i.
Также обратите внимание, что просто точка последовательности ничего не означает: порядок оценок не продиктован формой кода. Это диктуется семантическими правилами. В этом случае не существует семантического правила, говорящего, когда возникает побочный эффект присваивания в отношении оценки обоих его операндов или подвыражений этих операндов".
Заявление, написанное "смелым", смутило меня. Насколько я знаю:
"В определенных определенных точках последовательности выполнения, называемых точками последовательности, все побочные эффекты предыдущих оценок должны быть полными, и никаких побочных эффектов последующих оценок не должно быть".
Так как, операторы запятой также указывают порядок выполнения, побочный эффект я ++ был отменен, когда мы достигнем последнего i.He(Johannes), было бы правильно, если бы порядок оценки не был указан (но в случае запятой, это хорошо указано).
Итак, я просто хочу знать, вызывает ли (1) UB или нет?. Может ли кто-нибудь дать другое действительное объяснение?
Спасибо!
Ответы
Ответ 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; после его оценки появляется точка последовательности. Затем оценивается правый операнд; результат имеет свой тип и значение.
то есть. самый правый операнд операции с запятой нужно оценить, чтобы узнать значение и тип выражения для запятой (и значение правильного операнда для моего примера).
Итак, в этом случае "предыдущая точка последовательности" для побочного эффекта присваивания фактически была бы самой правой запятой. Возможная последовательность, упомянутая Йоханнесом, недействительна.
Пожалуйста, поправьте меня, если я ошибаюсь.