Программа ведет себя странно в онлайн-среде IDE

Я столкнулся с приведенной ниже программой на С++ (источник):

#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

Но в интерактивных IDE, таких как codechef, он дает следующий результат:

0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597

Почему цикл for не заканчивается на 300? Также эта программа всегда заканчивается на 4169. Почему 4169, а не какое-то другое значение?

Ответ 1

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

-faggressive-loop-optimizations

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

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


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

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

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

Ответ 2

"Undefined поведение undefined." (С)

Компилятор, используемый в кодеке, по-видимому, использует следующую логику:

  • Undefined поведение не может произойти.
  • i * 12345678 переполнение и результат в UB, если i > 173 (предполагается 32 бит int s).
  • Таким образом, i никогда не может превышать 173.
  • Таким образом, i < 300 является излишним и может быть заменен на true.

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

Ответ 3

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

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

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

Ответ 4

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