Программа ведет себя странно на онлайн IDEs
я столкнулся с приведенной ниже программой C++ (источник):
#include <iostream>
int main()
{
for (int i = 0; i < 300; i++)
std::cout << i << " " << i * 12345678 << std::endl;
}
это выглядит как простая программа и дает правильный результат на моей локальной машине, т. е. что-то вроде:
0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574
но, на онлайн Иды, как codechef, это дает следующий результат:
0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597
почему бы и нет for
цикл заканчивается на 300? Также эта программа всегда заканчивается на 4169
. Почему 4169
, а не какое-то другое значение?
4 ответа:
Я собираюсь предположить, что онлайн-компиляторы используют GCC или совместимый компилятор. Конечно, любой другой компилятор также может выполнять ту же оптимизацию, но документация GCC хорошо объясняет, что он делает:
-faggressive-loop-optimizations
этот параметр указывает оптимизатору циклов использовать языковые ограничения для получения границ числа итераций цикла. Это предполагает, что код цикла не вызывает неопределенное поведение, например вызывая переполнение целого числа со знаком или выход из связанного массива. Границы для числа итераций цикла используются для управления разверткой и отслаиванием цикла, а также для оптимизации тестов выхода из цикла. Этот параметр включен по умолчанию.
эта опция просто позволяет делать предположения, основанные на случаях, когда UB доказан. Чтобы воспользоваться этими предположениями, возможно, потребуется включить другие оптимизации, такие как постоянное сворачивание.
переполнение целого числа со знаком имеет неопределенное поведение. Оптимизатор смог доказать, что любое значение
i
больше 173 вызовет UB, и поскольку он может предположить, что UB нет, он также может предположить, чтоi
никогда не превышает 173. Затем он может дополнительно доказать, чтоi < 300
всегда истинно, и поэтому условие цикла можно оптимизировать прочь.Почему 4169, а не какое-то другое значение?
эти сайты, вероятно, ограничивают количество выходных строк (или символов или байтов) они покажите и разделите один и тот же предел.
"неопределенное поведение неопределено." (c)
компилятор, используемый на codechef, похоже, использует следующую логику:
- неопределенное поведение не может произойти.
i * 12345678
переполнения и результаты в UB ifi > 173
(при условии 32 битint
s).- таким образом,
i
не может превышать173
.i < 300
является излишним и может быть замененоtrue
.сам цикл кажется быть бесконечным. По-видимому, codechef просто останавливает программу после определенного количества времени или усекает вывод.
вы призываете неопределенное поведение вероятно, на 174-й итерации внутри
for
петля как Максint
значение, вероятно,2147483647
пока174 * 123456789
выражение2148147972
который является неопределенным поведением, поскольку нет переполнения целого числа со знаком. Таким образом, вы наблюдаете эффекты UB, особенно с компилятором GCC с флагами оптимизации, установленными в вашем случае. Вероятно, компилятор предупредил бы вас об этом, выпустив следующее внимание:warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]
удалить (
-O2
) флаги оптимизации для наблюдения за различными результатами.