Точки последовательности и побочные эффекты: Тихое изменение в C11?
C99 §6.5 Выражения
(1) Выражение представляет собой последовательность операторов и операндов, которая задает вычисление значения или обозначает объект или функцию или генерирует побочные эффекты или выполняет их комбинацию.
(2) Между предыдущей и следующей точкой последовательности объект должен иметь свое сохраненное значение, измененное не более одного раза путем оценки выражения. 72) Кроме того, предыдущее значение должно быть прочитано только для определить сохраненное значение. 73)
со сносками
72) Флаг состояния с плавающей запятой не является объектом и может быть установлен более одного раза в выражении.
73) Этот параграф отображает выражения выражения undefined, такие как
i = ++i + 1;
a[i++] = i;
позволяя
i = i + 1;
a[i] = i;
где C11 §6.5 изменен на (текст (1) имеет добавление):
(1) [...] Вычисления значений операндов оператора секвенируются перед вычислением значения результата оператора.
(2) Если побочный эффект скалярного объекта не зависит от другого побочного эффекта для одного и того же скалярного объекта или вычисления значения с использованием значения одного и того же скалярного объекта, поведение undefined. Если существует несколько допустимых порядков подвыражений выражения, поведение undefined, если такой какой-либо побочный эффект происходит в любом из упорядочений. 84)
где сноска 84 в C11 такая же, как 73 на C99.
Я немного смущен... Я читал C11 (2) как "[...] либо (другой побочный эффект для одного и того же скалярного объекта), либо (вычисление значения с использованием значения одного и того же скалярного объекта) [...]", который, кажется, даже не позволяет foo = ++i
(есть побочный эффект, и мы используем значение, зависящее от измененного объекта). Я не носитель языка, однако, было бы неплохо, если бы я мог сказать, как это предложение должно быть "разобрано". Я понимаю C99, но я не совсем понимаю формулировку C11.
В любом случае, фактический вопрос: это изменение от C99 до C11, или эквивалентны эти формулировки? И если да, то почему это было изменено? А если нет, может ли кто-нибудь привести пример выражения, которое является UB в C99, но не на C11 или наоборот?
Ответы
Ответ 1
C11 (а также С++ 11) полностью переработал формулировку последовательности, поскольку C11 теперь имеет потоки, и он должен был объяснить, что последовательность между потоками, которые обращаются к тем же данным, означает. Цель комитета состояла в том, чтобы сохранить совместимость вещей с C99 для случая, когда есть только один поток выполнения.
Посмотрим на версию C99:
-
Между предыдущей и следующей точкой последовательности
-
объект
-
должен иметь
-
сохраненное значение, измененное не более одного раза
-
путем оценки выражения.
по сравнению с новым текстом
Если побочный эффект на
разное terminolgie для 4, изменение сохраненного значения
скалярный объект
ограничение предыдущей формулировки в 2. Новый текст говорит только
что-то о скалярных объектах
не имеет никакого значения относительно либо
unsequenced - это обобщение концепции в 1., что два утверждения
были разделены точкой последовательности. Подумайте о двух видах, которые изменяют
те же данные без использования блокировки или чего-то подобного.
другой побочный эффект на том же скалярном объекте
объект разрешается изменять только один раз
или значение вычисление с использованием значения одного и того же скалярного объекта,
или чтение значения может не отображаться одновременно с модификацией
поведение undefined.
"Должен" в 3. говорит это неявно. Все "должны" привести к UB, если
они не выполняются.
Ответ 2
Я немного смущен... Я читал C11 (2) как "[...] либо (другой побочный эффект для одного и того же скалярного объекта), либо (вычисление значения с использованием значения одного и того же скалярного объекта) [...]", который, кажется, даже не позволяет foo = ++i
(есть побочный эффект, и мы используем значение, зависящее от измененного объекта).
Если вы внимательно прочитали стандартную цитату
Если побочный эффект на скалярном объекте не имеет никакого значения относительно другого другого побочного эффекта для одного и того же скалярного объекта или вычисления значения с использованием значения одного и того же скалярного объекта, поведение undefined. Если существует несколько допустимых порядков подвыражений выражения, поведение undefined, если такой какой-либо побочный эффект имеет место в любом из порядков. 84)
то вы обнаружите, что ваша формулировка должна быть:
Если побочный эффект на скалярном объекте не влияет на (другой побочный эффект на тот же скалярный объект) или (вычисление значения с использованием значения тот же скалярный объект).
Это означает, что foo = ++i
- это определенный оператор. Это правда, что есть побочный эффект на i
(также на foo
), но для объекта i
здесь нет непересекающегося.
Ответ 3
Это объяснение foo = ++i
, но на самом деле не ответ на вопрос.
Приращение префикса определяется в терминах назначения соединения, см. 6.5.3/2
Выражение ++E
эквивалентно (E+=1)
Для назначения вообще существует гарантия в 6.5.16/3
Побочным эффектом обновления сохраненного значения левого операнда является упорядочивается после вычисления значений левого и правого операндов. Оценки операнды не подвержены влиянию.
Итак, foo = ++i
эквивалентно foo = (i+=1)
. Внутренний i+=1
требует изменения последовательности i
после вычисления i+1
. Полученное значение выражения (i+=1)
указано в 6.5.16/3 как:
Выражение присваивания имеет значение левого операнда после присвоения, но не является lvalue.
Кажется, что это требует, чтобы вычисление значения i+=1
было упорядочено после модификации i
, а в С++ 11 это даже гарантировано явно [expr.ass]/1
Во всех случаях назначение упорядочивается после значения вычисление правого и левого операндов и перед вычислением значения выражения присваивания.
(что яснее для меня, но я знаю, что С++ намного лучше, чем C)
Модификация i
секвенирована перед вычислением значения i+=1
, поэтому мы не имеем UB, обращаясь к значению ++i
в foo = ++i
(поскольку вычисление значения левого и правого операндов foo = x
секвенированы до модификации foo
).
Ответ 4
Насколько я понимаю,
Если побочный эффект на скалярном объекте не влияет на... вычисление значения с использованием значения одного и того же скалярного объекта
здесь не применяется из-за (1), который гласит, что
Вычисления значений операндов оператора секвенируются перед вычислением значения результата оператора.
Другими словами, результат определяется как "прийти позже", т.е. е. он секвенирован.