Является ли поведение 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:

  • # P4 #
    # P5 # # P6 # # P7 #
  • # P8 #
    # P9 # # P10 #
  • # P11 #
    # P12 # # P13 #
  • # P14 #
    # P15 #
  • # P16 #
    # P17 #
  • # P18 #
    # P19 #

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

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

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

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

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

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

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


person user1480833    schedule 25.06.2012    source источник


Ответы (1)


[Мой ответ основан на более простом стандарте C99 и на том факте, что крайне маловероятно, что C11 внесет критическое изменение:]

Такое поведение этого кода четко определено: main возвращает 0. Точка последовательности находится сразу после полного выражения в операторе return (см. C99, Приложение C), поэтому побочные эффекты i++ вступают в силу до присвоения i в main.

person Oliver Charlesworth    schedule 25.06.2012
comment
Спасибо за вполне удовлетворительный ответ. Несмотря на то, что обработка последовательности в C99 в целом намного менее сложна и более неявна, чем в C11, для этого примера она на самом деле более явная; Я согласен с тем, что было бы очень сложно интерпретировать C99 так, чтобы он не гарантировал, что main вернет 0. - person user1480833; 26.06.2012