C11 разъясняет ответ на этот вопрос, в черновик стандартного раздела C11 6.8.5
Итерационные заявления добавлен следующий абзац:
оператор итерации, управляющее выражение которого не является константой
выражение, 156) , которое не выполняет никаких операций ввода / вывода, не
доступ к изменчивым объектам и не выполняет никакой синхронизации или атомарного
операции в его теле, контролируя выражение или (в случае
для утверждения) его выражение-3, может быть принято реализацией
прекратить. 157)
и сноска 157
гласит:
Это предназначено для разрешения преобразований компилятора, таких как удаление пустых циклов, даже когда
прекращение не может быть доказано.
Итак, ваш конкретный пример:
while(1)
/* noop */;
не подходит для оптимизации, поскольку управляющее выражение является константным выражением.
Бесконечные циклы как UB
Так почему компиляторам разрешено оптимизировать бесконечные циклы с исключением, приведенным выше, хорошо, Ханс Бём предлагает обоснование для создания неопределенного поведения бесконечных циклов в: N1528: Почему неопределенное поведение для бесконечных циклов? , Следующая цитата дает хорошее представление о проблеме:
Как правильно указывает N1509, текущий проект по существу дает
неопределенное поведение бесконечных циклов в 6.8.5p6. Основная проблема для
при этом он позволяет коду перемещаться по потенциально
бесконечный цикл. Например, предположим, что у нас есть следующие циклы,
где count и count2 являются глобальными переменными (или имеют свой адрес
берется), а p - локальная переменная, адрес которой не был взят:
for (p = q; p != 0; p = p -> next) {
++count;
}
for (p = q; p != 0; p = p -> next) {
++count2;
}
Могут ли эти два цикла быть объединены и заменены следующим циклом?
for (p = q; p != 0; p = p -> next) {
++count;
++count2;
}
Без специального разрешения в 6.8.5p6 для бесконечных циклов это
будет запрещено: если первый цикл не заканчивается, потому что д
указывает на круговой список, оригинал никогда не пишет в count2. таким образом
он может быть запущен параллельно с другим потоком, который обращается или
Количество обновлений2. Это больше не безопасно с преобразованной версией
который делает доступ к count2, несмотря на бесконечный цикл. Таким образом
преобразование потенциально вводит гонку данных.
В подобных случаях очень маловероятно, что компилятор сможет
доказать завершение цикла; было бы понять, что д баллов
к ациклическому списку, который, как я считаю, находится за пределами возможностей большинства
обычные компиляторы, и часто невозможно без всей программы
информация.
C99
Поскольку C99 не имеет этого исключения, мы обратимся к правилу как если бы , описанному в разделе 5.1.2.3
, в котором в основном говорится, что компилятор должен только имитировать наблюдаемое поведение программы Требования следующие:
Минимальные требования к соответствующей реализации:
- В точках последовательности летучие объекты стабильны в том смысле, что предыдущие обращения
полные и последующие обращения еще не произошли.
- При завершении программы все данные, записанные в файлы, должны быть идентичны результату, который
Выполнение программы в соответствии с абстрактной семантикой привело бы к созданию.
- Динамика ввода и вывода интерактивных устройств осуществляется в соответствии с
7.19.3. Цель этих требований заключается в том, что небуферизованный или линейный буферизованный вывод
появиться как можно скорее, чтобы убедиться, что сообщения-подсказки действительно появляются до
программа, ожидающая ввода.
Строгое прочтение этого может позволить реализации оптимизировать бесконечный цикл. Мы, конечно, можем придумать сценарии, в которых оптимизация бесконечного цикла приведет к изменению наблюдаемого поведения:
while(1) ;
printf( "hello world\n" ) ;
Многие утверждают, что воздействие на завершение процесса также является наблюдаемым поведением, эта позиция взята из Компиляторы C опровергают последнюю теорему Ферма :
Компилятору предоставлена значительная свобода в том, как он реализует программу на C, но его выходные данные должны иметь то же внешне видимое поведение, что и программа при интерпретации «абстрактной машиной C», которая описана в стандарте. Многие знающие люди (в том числе и я) считают это тем, что говорят, что поведение при завершении программы не должно изменяться. Очевидно, что некоторые авторы компиляторов не согласны или не верят, что это имеет значение. Тот факт, что разумные люди не согласны с толкованием, может указывать на то, что стандарт С имеет недостатки.
Обновление
Я как-то пропустил продолжение этой статьи, Пересмотренные компиляторы и завершение , в которой говорится следующее о разделе 5.1.2.3
:
Второе требование - хитрое. Если речь идет о завершении программы, запущенной на абстрактной машине, то она встречается пусто, потому что наша программа не завершается. Если речь идет об окончании фактической программы, сгенерированной компилятором, то реализация C ошибочна, потому что данные, записанные в файлы (stdout - это файл), отличаются от данных, записываемых абстрактной машиной. (Это чтение из-за Ганса Бема; я не смог дразнить эту тонкость вне стандарта.)
Можно также привести более слабый аргумент, что необходимость создания вырезки в C11 для разрешения удаления пустых циклов подразумевает, что ранее это не было допустимой оптимизацией.
Это относится и к бесконечным циклам goto?
Я полагаю, что намерение состоит в том, что это также относится к бесконечным циклам goto. В C ++ это явно так, поскольку в разделе 1.10
[intro.multithread] написано:
Реализация может предполагать, что любой поток в конечном итоге выполнит одно из следующих действий:
- прекратить
- сделать вызов функции ввода / вывода библиотеки,
- получить доступ или изменить изменчивый объект, или
- выполнить операцию синхронизации или атомарную операцию.
и затем намерение, выраженное в N1528
, заключается в том, что стандарты C и C ++ согласуются:
Поскольку бэкэнды компилятора обычно совместно используются компиляторами C и C ++, представляется наиболее важным, чтобы WG14 и WG21 согласовали любое принятое решение. Альтернативы могут заключаться в особой обработке двух языков бэк-эндом или отключении оптимизаций при обработке кода C. Ни то, ни другое не кажется желательным.
и в конце говорит:
WG21 рассматривает улучшенную формулировку, которая делает обработку последовательной. Надеемся, что WG14 отследит любые возникающие изменения.
В настоящее время стандарт C11 не содержит аналогичных формулировок в разделе 5.1.2.4
Многопоточные выполнения и гонки данных , но, учитывая N1528
, разумно предположить, что компиляторы будут рассматривать бесконечные циклы goto как неопределенное поведение в C и C ++.
Примечание также см. Комментарий США 38 здесь и N3196 , к которым относится бумага, с которой было применено это изменение.