Я наткнулся на следующую программу на 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
Но в онлайн-IDE, таких как codechef , он дает следующий результат:
0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597
Почему for
цикл не заканчивается на 300? Также эта программа всегда завершается 4169
. Почему, 4169
а не какую-то другую ценность?
c++
undefined-behavior
integer-overflow
арпанмангал
источник
источник
Ответы:
Я предполагаю, что онлайн-компиляторы используют GCC или совместимый компилятор. Конечно, любой другой компилятор также может выполнять такую же оптимизацию, но документация GCC хорошо объясняет, что он делает:
Эта опция просто позволяет делать предположения на основе случаев, когда UB доказано. Чтобы воспользоваться этими предположениями, может потребоваться включить другие оптимизации, например сворачивание констант.
Знаковое целочисленное переполнение имеет неопределенное поведение. Оптимизатор смог доказать, что любое значение
i
больше 173 вызовет UB, и, поскольку он может предположить, что UB нет, он также может предположить, чтоi
оно никогда не будет больше 173. Затем он может дополнительно доказать, чтоi < 300
это всегда верно, и так что условие цикла можно оптимизировать.Эти сайты, вероятно, ограничивают количество выводимых строк (или символов, или байтов), которые они показывают, и имеют такое же ограничение.
источник
i
больше 173, то почему он не выдает предупреждение вместо того, чтобы выполнять бессмысленную оптимизацию?«Неопределенное поведение не определено».(c)
Компилятор, используемый в codechef, похоже, использует следующую логику:
i * 12345678
переполняется и приводит к UB, еслиi > 173
(при условии 32-битногоint
).i
никогда не может превышать173
.i < 300
является лишним и может быть заменен наtrue
.Сам цикл кажется бесконечным. По-видимому, codechef просто останавливает программу через определенное время или обрезает вывод.
источник
2^16
. По-видимому, это совпадение, что оба обрезают вывод до2^16
символов.Вы вызываете поведение undefined, вероятно, на 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
.источник