странное целочисленное поведение с gcc -O2

#include <stdio.h>
#include <limits.h>

void sanity_check(int x)
{
if (x < 0)
{
x = -x;
}
if (x == INT_MIN)
{
printf("%d == %d\n", x, INT_MIN);
}
else
{
printf("%d != %d\n", x, INT_MIN);
}
if (x < 0)
{
printf("negative number: %d\n", x);
}
else
{
printf("positive number: %d\n", x);
}
}

int main(void)
{
sanity_check(42);
sanity_check(-97);
sanity_check(INT_MIN);
return 0;
}

Когда я компилирую вышеупомянутую программу с gcc wtf.cЯ получаю ожидаемый результат:

42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 == -2147483648
negative number: -2147483648

Тем не менее, когда я собираю программу с gcc -O2 wtf.cЯ получаю другой вывод:

42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 != -2147483648
positive number: -2147483648

Обратите внимание на последние две строки. Что здесь происходит? Gcc 4.6.3 оптимизирует слишком охотно?

(Я также проверил это с g ++ 4.6.3, и я наблюдал такое же странное поведение, отсюда и тег C ++.)

9

Решение

Когда вы делаете — (INT_MIN) вы вызываете неопределенное поведение, так как этот результат не может поместиться в int.

gcc -O2 замечает, что x никогда не может быть отрицательным, и оптимизирует после этого. Это не заботится о том, что вы переполнили значение, поскольку оно не определено, и оно может обработать его так, как захочет.

15

Другие решения

Я думаю, что это может помочь вам, отсюда:Вот

-fstrict-перелив
Разрешить компилятору принимать строгие подписанные правила переполнения, в зависимости от компилируемого языка. Для C (и C ++) это означает, что переполнение при выполнении арифметики со знаковыми числами не определено, что означает, что компилятор может предположить, что этого не произойдет. Это допускает различные оптимизации. Например, компилятор будет предполагать, что выражение типа i + 10> i всегда будет истинным для подписанного i. Это предположение верно только в том случае, если переполнение со знаком не определено, так как выражение ложно, если переполнение i + 10 при использовании арифметики с дополнением до двух. Когда эта опция активна, любая попытка определить, будет ли переполнена операция с числами со знаком, должна быть тщательно записана, чтобы фактически не включать переполнение.
Эта опция также позволяет компилятору принимать строгую семантику указателя: если указатель на объект, то при добавлении смещения к этому указателю указатель на тот же объект не создается, добавление не определено. Это позволяет компилятору сделать вывод, что p + u> p всегда верно для указателя p и целого числа без знака u. Это предположение верно только потому, что обтекание указателя не определено, поскольку выражение ложно, если p + u переполняется с использованием арифметики, дополняющей два.

Смотрите также параметр -fwrapv. Использование -fwrapv означает, что целочисленное переполнение со знаком полностью определено: оно переносится. Когда используется -fwrapv, нет разницы между -fstrict-overflow и -fno-strict-overflow для целых чисел. С -fwrapv разрешены определенные типы переполнения. Например, если компилятор получает переполнение при выполнении арифметики с константами, переполненное значение может все еще использоваться с -fwrapv, но не иначе.

Опция -fstrict-overflow включена на уровнях -O2, -O3, -Os.

12

По вопросам рекламы [email protected]