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

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

i = ++ i + 1

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

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

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

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

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

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

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

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

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

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

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

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

Благодаря!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Хороший язык судоку. 🙂

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

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

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

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

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

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

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

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

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

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

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

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


  • Переопределение grails.views.default.codec = 'html' config обратно в 'none'
  • Regex lookahead, lookbehind и атомные группы
  • Объединить строки и выражения в заголовок сюжета
  • Как проверить, начинается ли строка с одного из нескольких префиксов?
  • Получить имя метода, используя выражение
  • Что такое i = (i, ++ i, 1) + 1; делать?
  • В чем разница между анонимными методами (C # 2.0) и lambda-выражениями (C # 3.0)?
  • Давайте будем гением компьютера.