Является ли поведение i = post_increment_i() заданным, неуказанным или неопределенным?

Рассмотрим следующую программу C:

int i = 0;

int post_increment_i() { return i++; }

int main() {
    i = post_increment_i();
    return i;
}

Что касается версии стандарта C 2011 г. (известной как C11), какая из следующих альтернатив верна :

  1. C11 гарантирует, что main возвращает 0.
  2. C11 гарантирует, что main возвращает либо 0, либо 1.
  3. Поведение этой программы не определено согласно C11

Соответствующие фрагменты из стандарта C11:

  • 5.1.2.3 Выполнение программы

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

    Предшествующая последовательность представляет собой асимметричное, транзитивное, парное отношение между оценками. выполняется одним потоком, что вызывает частичный порядок среди этих оценок. Для любых двух вычислений A и B, если A расположено перед B, то выполнение A должно предшествовать выполнению B. (И наоборот, если A расположено перед B, то B последовательно после А.) Если А не находится в последовательности до или после В, то А и В являются непоследовательность. Оценки A и B неопределенно упорядочены, когда A упорядочена либо до, либо после B, но не указано, что именно. 13Наличие точки следования между оценкой выражений A и B подразумевает, что каждое вычисление значения и побочный эффект, связанный с A, упорядочивается перед каждым вычислением значения и побочным эффектом связанный с B. (Сводка точек последовательности приведена в приложении C.)

    13) Выполнение неупорядоченных оценок может чередоваться. Оценки с неопределенной последовательностью не могут чередоваться, но могут выполняться в любом порядке.

  • 6.5 Выражения

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

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

  • 6.5.2.2 Вызовы функций

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

    94) Другими словами, выполнение функций не «чередуется» друг с другом.

  • 6.5.2.4 Постфиксные операторы инкремента и декремента

    Результатом постфиксного оператора ++ является значение операнда. Как побочный эффект, значение объекта операнда увеличивается (то есть значение 1 соответствующего типа добавлено к нему). [...] Вычисление значения результата выполняется до побочного эффекта обновление сохраненного значения операнда. Что касается неопределенно упорядоченного вызов функции, операция postfix ++ является одной оценкой.

  • 6.5.16 Присваивания

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

  • 6.8 Операторы и блоки

    Полное выражение — это выражение, которое не является частью другого выражения или декларатора. Каждое из следующего является полным выражением: [...] выражение в операторе выражения;[...] (необязательное) выражение в возврате утверждение. Между вычислением полного выражения и оценка следующего полного выражения, подлежащего оценке.

Три альтернативы выше соответствуют следующим трем случаям, соответственно:

  1. Побочный эффект оператора постфиксного приращения упорядочен до присваивания в main.
  2. Побочный эффект оператора постфиксного приращения упорядочен либо до, либо после присваивания в main, и C11 не определяет, какой именно. (Другими словами, два побочных эффекта расположены в неопределенной последовательности.)
  3. Два побочных эффекта не имеют последовательности.

Кажется, что первая альтернатива верна, следуя следующей цепочке рассуждений:

  • Рассмотрим правило Каждое вычисление в вызывающей функции (включая другие вызовы функций), которые иначе не упорядочены до или после выполнение тела вызываемой функции неопределенно упорядочено относительно выполнение вызываемой функции. в 6.5.2.2. Предположение A: побочным эффектом оператора присваивания в main является такая «оценка». Предположение B: Фраза «выполнение вызываемой функции» включает в себя как вычисление значения оператора постфиксного приращения, так и побочный эффект оператора постфиксного приращения.Из этих предположений и приведенного выше правила следует, что либо I) вычисление значения и побочный эффект оператора постфиксного приращения оба упорядочены перед побочным эффектом оператора присваивания в main, либо II) вычисление значения и побочный эффект оператора приращения постфикса оба упорядочены после побочного эффекта оператора присваивания в main.

  • Рассмотрим правило Побочным эффектом обновления сохраненного значения левого операнда является последовательно после вычислений значений левого и правого операндов. Это правило исключает случай I выше. Отсюда следует, что имеет место случай II. QED

В целом, это выглядит довольно сильным аргументом. Кроме того, это соответствует тому, что можно было бы интуитивно счесть наиболее вероятной альтернативой.

Однако он опирается на специфические интерпретации терминов «вычисление» и «выполнение вызываемой функции» (предположения A и B) и не совсем прямолинейный ход рассуждений, поэтому я хотел выложить его, чтобы увидеть если у людей есть основания полагать, что эта интерпретация неверна. Обратите внимание, что сноска 94 эквивалентна этой интерпретации только в том случае, если она применяется также в том смысле, что вызывающий не чередуется с вызываемым, что, в свою очередь, подразумевает, что «чередование» означает чередование в «абабском» смысле, поскольку очевидно, что вызывающий абонент чередуется с вызываемым. вызываемый в более слабом смысле «аба».Кроме того, варианты 2 и 3 кажутся правдоподобными в сценарии, когда компилятор встраивает функцию, а затем выполняет те же виды оптимизации, которые объясняют, почему выражение i = i++имеет неопределенное поведение.

6
задан melpomene 14 March 2019 в 19:28
поделиться