Насколько губительно целочисленное переполнение в C ++? - PullRequest
22 голосов
/ 27 января 2012

Мне было просто интересно, насколько разрушительным является целочисленное переполнение. Возьмите следующий пример программы:

#include <iostream>

int main()
{
    int a = 46341;
    int b = a * a;
    std::cout << "hello world\n";
}

Поскольку a * a переполняется на 32-битных платформах, а целочисленное переполнение вызывает неопределенное поведение, есть ли у меня какие-либо гарантии того, что hello world действительно появится на моем экране?


Я удалил «подписанную» часть из своего вопроса на основе следующих стандартных цитат:

(§5 / 5 C ++ 03, §5 / 4 C ++ 11) Если во время вычисления выражения результат не определен математически или не находится в диапазоне представимых значений для его типа, поведение не определено.

(§3.9.1 / 4) Целые числа без знака, объявленные unsigned, должны подчиняться законам арифметики по модулю 2 ^ n, где n - количество битов в представлении значения этого конкретного размера целого числа. Это означает, что арифметика без знака не переполняется , поскольку результат, который не может быть представлен результирующим целочисленным типом без знака, уменьшается по модулю на число, которое на единицу больше наибольшего значения, которое может быть представлено результирующим целочисленным типом без знака .

Ответы [ 3 ]

21 голосов
/ 27 января 2012

Как отметил @Xeo в комментариях (я на самом деле поднял это в чате C ++ сначала):
Неопределенное поведение действительно означает это, и оно может ударить вас, когда вы меньше всегоожидайте этого.

Лучший пример этого здесь: Почему переполнение целых чисел в x86 с GCC вызывает бесконечный цикл?

В целых числах со знаком x86Переполнение - это просто обход.Обычно можно ожидать, что то же самое произойдет в C или C ++ .Однако компилятор может вмешаться - и использует неопределенное поведение как возможность оптимизировать .

В примере, взятом из этого вопроса:

#include <iostream>
using namespace std;

int main(){
    int i = 0x10000000;

    int c = 0;
    do{
        c++;
        i += i;
        cout << i << endl;
    }while (i > 0);

    cout << c << endl;
    return 0;
}

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

8 голосов
/ 27 января 2012

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

Редактировать: Обратите внимание, что у gcc есть опция -ftrapv (но у меня она не работает).

5 голосов
/ 27 января 2012

Есть два взгляда на неопределенное поведение. Существует мнение, что его нужно собирать для странного оборудования и других особых случаев, но обычно оно должно вести себя разумно. И есть мнение, что все может случиться. И в зависимости от источника UB некоторые придерживаются разных мнений.

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

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

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