Программа ведет себя странно на онлайн 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 74

4 ответа:

Я собираюсь предположить, что онлайн-компиляторы используют GCC или совместимый компилятор. Конечно, любой другой компилятор также может выполнять ту же оптимизацию, но документация GCC хорошо объясняет, что он делает:

-faggressive-loop-optimizations

этот параметр указывает оптимизатору циклов использовать языковые ограничения для получения границ числа итераций цикла. Это предполагает, что код цикла не вызывает неопределенное поведение, например вызывая переполнение целого числа со знаком или выход из связанного массива. Границы для числа итераций цикла используются для управления разверткой и отслаиванием цикла, а также для оптимизации тестов выхода из цикла. Этот параметр включен по умолчанию.

эта опция просто позволяет делать предположения, основанные на случаях, когда UB доказан. Чтобы воспользоваться этими предположениями, возможно, потребуется включить другие оптимизации, такие как постоянное сворачивание.


переполнение целого числа со знаком имеет неопределенное поведение. Оптимизатор смог доказать, что любое значение i больше 173 вызовет UB, и поскольку он может предположить, что UB нет, он также может предположить, что i никогда не превышает 173. Затем он может дополнительно доказать, что i < 300 всегда истинно, и поэтому условие цикла можно оптимизировать прочь.

Почему 4169, а не какое-то другое значение?

эти сайты, вероятно, ограничивают количество выходных строк (или символов или байтов) они покажите и разделите один и тот же предел.

"неопределенное поведение неопределено." (c)

компилятор, используемый на codechef, похоже, использует следующую логику:

  1. неопределенное поведение не может произойти.
  2. i * 12345678 переполнения и результаты в UB if i > 173 (при условии 32 бит ints).
  3. таким образом, i не может превышать 173.
  4. i < 300 является излишним и может быть заменено true.

сам цикл кажется быть бесконечным. По-видимому, codechef просто останавливает программу после определенного количества времени или усекает вывод.

вы призываете неопределенное поведение вероятно, на 174-й итерации внутри for петля как Макс int значение, вероятно,2147483647 пока 174 * 123456789 выражение 2148147972 который является неопределенным поведением, поскольку нет переполнения целого числа со знаком. Таким образом, вы наблюдаете эффекты UB, особенно с компилятором GCC с флагами оптимизации, установленными в вашем случае. Вероятно, компилятор предупредил бы вас об этом, выпустив следующее внимание:

warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]

удалить (-O2) флаги оптимизации для наблюдения за различными результатами.

компилятор может предположить, что неопределенное поведение не произойдет, и поскольку знаковое переполнение является UB, он может предположить, что никогда i * 12345678 > INT_MAX, таким образом i <= INT_MAX / 12345678 < 300 и поэтому уберите чек i < 300.