опция printf и gcc -O изменяют возвращаемое значение - PullRequest
2 голосов
/ 03 июня 2019

Я пытаюсь понять, как опция gcc -O и printf могут изменить возвращаемое значение.

в приведенном ниже коде isTmax - это моя функция, которая возвращает 1, если на входе находится наибольшее значение типа int.

int isTmax(int x);
int main(void)
{
    printf("%d\n", isTmax(0x7fffffff));
}

int isTmax(int x)
{
    int temp = x + x + 2;
    int result = !temp & (!!(~x));
    return result;
}

он отлично работал, когда я скомпилировал его без опции gcc.

, но с опцией -O я получил 0 для всех целочисленных значений.

поэтому я добавил printf для проверки значений, а затем скомпилировал его с опцией -O.

int isTmax(int x)
{
    int temp = x + x + 2;
    int result = !temp & (!!(~x));
    printf("x : %x, temp : %x, !temp : %x\n", x, temp , !temp); 
    return result;
}

И вдруг снова работает.

Я хочу знать, почему возвращаемое значение меняется.

1 Ответ

3 голосов
/ 03 июня 2019

Как было отмечено в комментариях, это связано с неопределенным поведением.

Современные компиляторы могут (ab) использовать неопределенное поведение для упрощения, сокращения и ускорения скомпилированного кода без нарушения каких-либо правил. Я предлагаю вам прочитать эту прекрасную статью об опасностях UB.

Трудно понять, что именно происходит, потому что оптимизатор становится чрезвычайно сложным, но вот пример того, что компилятор может делать с вашей функцией.

(1) int isTmax(int x)
(2) {
(3)     int temp = x + x + 2;
(4)     int result = !temp & (!!(~x));
(5)     return result;
(6) }

В строке 3 вы складываете два целых числа со знаком. Если ваша программа вызывает эту функцию только один раз с 0x7fffffff, компилятор уже знает, что код производит только целочисленное переполнение. Там есть поворот с этим кодом; так как вы добавляете 2 к операции переполнения, компилятор может предположить, что значение будет положительным и больше двух, ради того, что будет дальше.

В строке 4 !temp переводится в логическую константу false, поскольку предполагается, что temp является положительным значением. Далее and; поскольку левое значение равно false (или 0), result всегда заканчивается 0. Если результат всегда равен нулю, оптимизатор также может удалить все переменные и операции.

По сути, после оптимизации ваша функция выглядит следующим образом:

int isTmax(int x)
{
    return 0;
}

Добавляя printf внутри функции, вы заставляете оптимизатор де-оптимизации, и в итоге вы получаете тот же код, который вы получаете, когда не оптимизируете свою программу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...