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

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

i = ++ я + 1

вызывает UB (Undefined Behavior) или нет.

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

Я был вовлечен в обсуждение с Johannes Schaub в том же самом потоке. По его словам,

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

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

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

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

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

Оператор, записанный в "полужирном", смутил меня.Насколько мне известно:

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

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

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

Спасибо!

22
задан Community 23 May 2017 в 11:44
поделиться

4 ответа

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

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

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

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

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

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

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

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

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

15
ответ дан 29 November 2019 в 05:31
поделиться

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

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

Тем не менее, судоку хороший язык. : -)

edit: Здесь есть более подробное объяснение .

5
ответ дан 29 November 2019 в 05:31
поделиться

I believe that the following expression definitely has undefined behaviour.

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

The reason is that the comma operator specifies sequence points between the subexpressions in parentheses but does not specify where in that sequence the evaluation of the left hand operand of + occurs. One possibility is between the sequence points surrounding i++ and this violates the 5/4 as i is written to between two sequence points but is also read twice between the same sequence points and not just to determine the value to be stored but also to determine the value of the first operand to the + operator.

This also has undefined behaviour.

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

Now, I am not so sure about this statement.

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

Although the same principals apply, i must be "evaluated" as a modifiable lvalue and can be done so at any time, but I'm not convinced that its value is ever read as part of this. (Or is there another restriction that the expression violates to cause UB?)

The sub-expression (i, i++, i) happens as part of determining the value to be stored and that sub-expression contains a sequence point after the storage of a value to i. I don't see any way that this wouldn't require the side effect of i++ to be complete before the determination of the value to be stored and hence the earliest possible point that the assignment side effect could occur.

After this sequnce point i's value is read at most once and only to determine the value that will be stored back to i, so this last part is fine.

6
ответ дан 29 November 2019 в 05:31
поделиться

Вначале я был сбит с толку относительно заявления Йоханнеса (литб), но он упомянул это в:

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


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

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

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

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

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

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

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

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

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


-1
ответ дан 29 November 2019 в 05:31
поделиться