почему я получаю разные результаты с одинаковыми условиями в цикле for? - PullRequest
0 голосов
/ 27 августа 2018

Я застрял, когда пытался использовать цикл for для решения проблемы.

Вот мой упрощенный код:

int main(int argc, const char * argv[])
{
    std::vector<int> a;
    a.push_back(2333);
    int n = 10, m = 10;
    for(int i=0; i< -1; i++)
        m--;
    std::cout<<m<<endl;
    for(int j=0; j<a.size()-2; j++)
        n--;
    std::cout<<n<<endl;
    return 0;
}

Очевидно, a.size() = 1, поэтому эти дваКонечные условия должны быть одинаковыми.Однако, когда я запустил свой код на Xcode 9.4.1, я неожиданно обнаружил, что m = 10 и n = 11.И я обнаружил, что время, необходимое для получения значения n, намного больше, чем m.

Почему я получу такой результат?Любая помощь будет оценена.

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Значение, возвращаемое size(), равно std::size_t, что является целым типом без знака.Это означает, что он может представлять только неотрицательные числа, и если вы выполняете операцию, которая приводит к получению отрицательного числа, он будет округлен до максимально возможного значения, как в модульной арифметике.

Здесь, 2 - 1равно -1, что означает 2^32 - 1 в 32-битной системе.Когда вы пытаетесь вычесть 2^32 - 1 из 10, вы вызываете переполнение целого числа со знаком, поскольку минимальное значение 32-разрядного целого числа равно -2^31.Целочисленное переполнение / недополнение со знаком равно неопределенное поведение , поэтому может произойти все что угодно.

В этом случае кажется, что переполнение обернуто до максимального значения, как целое число со знаком.Таким образом, результатом будет 10 - (2^32 - 1) + 2^32, что равно 11. Мы добавляем 2^32 для имитации обтекания нижнего потока.Другими словами, после 2^31 + 10 -ой итерации цикла n является минимально возможным значением в 32-разрядном целом числе.Следующая итерация вызывает обтекание, поэтому n теперь равно 2^31 - 1.Затем оставшиеся 2^31 - 12 итераций уменьшаются n до 11.

Опять переполнение / недополнение со знаком является неопределенным поведением, поэтому не удивляйтесь, если из-за этого произойдет что-то странное, особенно с современным компиляторомоптимизаций.Например, вся ваша программа может быть «оптимизирована», чтобы абсолютно ничего не делать, поскольку она всегда будет вызывать UB.Вы даже не гарантированно видите вывод std::cout<<m<<endl;, даже если UB вызывается после выполнения этой строки.

0 голосов
/ 27 августа 2018

Значение, возвращаемое a.size(), это тип size_t, который является целым без знака, потому что не было бы никаких причин иметь отрицательный размер.Если вы сделаете 1-2 для чисел без знака, оно перевернется и станет значением, близким к максимальному значению для целого числа без знака, и цикл будет выполняться довольно долго или даже не остановится, поскольку целое число со знаком не может быть большечем верхняя половина значений без знака.Это зависит от правил сравнения подписанных и неподписанных, которые я точно не помню на месте.

Использование отладчика и проверка правильности типов (ваш компилятор должен упомянуть несоответствие со знаком и без знака здесь) помогает определить эти случаи.

...