Последовательность операторов присваивания в выражениях C11

Вступление

Стандарт C11 (ISO/IEC 9899:2011) ввел новое определение последовательности побочных эффектов в выражении ( см. Связанный вопрос). Концепция точки последовательности была дополнена последовательностью до и последовательностью после отношений, которые теперь являются основой для всех определений.

Раздел 6.5 "Выражения", пункт 2 гласит:

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

Далее в разделе 6.5.16 "Операторы присваивания", пункт 3, говорится:

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

проблема

Первый цитируемый абзац (6.5/2) подтверждается двумя примерами (такими же, как в стандарте C99):

Первый пример

a[i++] = i;  //! undefined
a[i] = i;    //  allowed

Это можно легко объяснить с помощью определений:

  1. Если побочный эффект на скалярный объект не секвенирован относительно (...) вычисления значения с использованием значения того же скалярного объекта, поведение не определено. (6,5/2),
  2. Оценки операндов не являются последовательными. [в рамках задания] (6.5.16/3).

Итак, побочный эффект i++ (LHS) не секвенируется с i (RHS), который дает неопределенное поведение.

Второй пример

i = ++i + 1; //! undefined
i = i + 1;   //  allowed

Этот код, однако, похоже, приводит к определенному поведению в обоих случаях:

  1. побочный эффект обновления сохраненного значения левого операнда секвенируется после вычислений значения левого и правого операнда.

Итак, исполнение ++i + 1должен предшествовать побочный эффект обновления iЭто означает, что не существует побочного эффекта для скалярного объекта, не секвенированного относительно другого побочного эффекта для того же скалярного объекта или вычисления значения с использованием значения того же скалярного объекта.

Вопрос

Эти примеры легко объяснить с помощью терминов и определений, представленных в стандарте C99 ( см. Соответствующий вопрос). Но почему i = ++i + 1 не определено в соответствии с терминологией C11?

3 ответа

Решение

Обновить

Я изменяю свой ответ здесь, это не очень хорошо определено в C11, хотя это в C++11. Ключевым моментом здесь является то, что результат ++i не является lvalue и, следовательно, не требует преобразования lvalue в rvalue после ++i оценивается и поэтому мы не можем быть уверены, что результат ++i будет прочитано позже. Это отличается от C++, и поэтому отчет о дефектах, который я изначально связал, зависит от этого важного факта:

[...] выражение lvalue ++i, а затем выполнить преобразование lvalue в rvalue для результата. гарантирует, что побочный эффект увеличения секвенируется перед вычислением операции сложения [...]

мы можем увидеть это, перейдя в раздел проекта стандарта C11 6.5.3.1 Префиксный оператор увеличения и уменьшения, который говорит:

[...] Выражение ++E эквивалентно (E+=1).[...]

а затем раздел 6.5.16 Операторы присваивания, которые говорят (выделение мое в будущем):

Оператор присваивания сохраняет значение в объекте, обозначенном левым операндом. Выражение присваивания имеет значение левого операнда после присваивания111, но не является lvalue. [...]

и сноска 111 говорит:

Реализация может считывать объект для определения значения, но не обязательна, даже если объект имеет тип volatile-qualified.

Нет необходимости читать объект, чтобы определить его стоимость, даже если он изменчив.

Оригинальный ответ

Насколько я могу судить, это на самом деле хорошо определено, и этот пример был удален из проекта стандарта C++, который использует похожий язык. Мы можем видеть это в 637. Правила последовательности и пример не согласны, который говорит:

следующее выражение все еще перечислено как пример неопределенного поведения:

i = ++i + 1;

Однако, похоже, что новые правила последовательности делают это выражение четко определенным:

и было решено использовать пример с префиксом и использовать пример с постфиксом, который явно не определен:

Измените пример в пункте 1.9 [intro.execution] следующим образом:

i = ++i i ++ + 1; // поведение не определено

Стандарт предусматривает присвоение (6.5.16), поскольку вы цитируете правильно

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

(Оператор приращения не отличается, это просто скрытое присваивание)

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

Но почему i = ++i + 1 не определено в соответствии с терминологией C11?

С11 говорит, что побочный эффект слева i последовательность, но не вычисление значений (оценки) слева и справа i,
Очевидно, что побочный эффект на LHS будет иметь место после оценки выражений на LHS и RHS.
Чтобы объяснить это, лучший пример может быть

int i = 1;
i = i++ + 3;

(Сначала давайте предположим, что этот пример не будет вызывать UB). Теперь окончательная стоимость i может быть 4 или же 2,
Случай 1
Оставил i извлекается, а затем увеличивается и 3 добавляется к нему и, наконец, 4 назначен на i,
Случай 2
Оставил i извлекается, а затем 3 добавляется к нему, а затем 4 назначен на i и наконец i увеличивается В этом случае окончательное значение i является 2,
Хотя побочный эффект слева i Последовательность окончательного значения сохраняется в i не определяется, т. е. это не обязательно с помощью назначения и, следовательно, побочный эффект на i не секвенировано

Другие вопросы по тегам