MSVC ++: странность с неподписанными целочисленными значениями и переполнением - PullRequest
8 голосов
/ 24 марта 2009

У меня есть следующий код:

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    string a = "a";
    for(unsigned int i=a.length()-1; i+1 >= 1; --i)
    {
        if(i >= a.length())
        {
            cerr << (signed int)i << "?" << endl;
            return 0;
        }
    }
}

Если я скомпилирую в MSVC с полной оптимизацией, я получу вывод «-1?». Если я скомпилирую в режиме отладки (без оптимизации), я не получу вывод (ожидается).

Я думал, что стандарт гарантирует, что целые числа без знака переполнены предсказуемым образом, поэтому, когда i = (unsigned int) (- 1), i + 1 = 0 и условие цикла i + 1> = 1 не выполняется. Вместо этого тест как-то проходит. Это ошибка компилятора, или я делаю что-то неопределенное где-то?

Ответы [ 4 ]

8 голосов
/ 24 марта 2009

Я помню эту проблему в 2001 году. Я поражен, что она все еще там. Да, это ошибка компилятора.

Оптимизатор видит

i + 1 >= 1;

Теоретически, мы можем оптимизировать это, поместив все константы на одной стороне:

i >= (1-1);

Поскольку i не подписано, оно всегда будет больше или равно нулю.

См. Обсуждение этой группы новостей здесь .

4 голосов
/ 24 марта 2009

ISO14882: 2003, раздел 5, пункт 5:

Если во время вычисления выражения результат не определен математически или не находится в диапазоне представимых значений для его типа , поведение не определено, если только такое выражение не является константным выражением (5.19 ), в этом случае программа некорректна.

(Акцент мой.) Так что да, поведение не определено. Стандарт не дает никаких гарантий поведения в случае целочисленного переполнения / недополнения.

Редактировать: Стандарт кажется немного противоречивым по этому вопросу в другом месте.

Раздел 3.9.1.4 гласит:

Целые числа без знака, объявленные как без знака, должны подчиняться законам арифметики по модулю 2 n, где n - количество бит в представлении значения этого конкретного размера целого числа.

Но в разделе 4.7.2 и .3 говорится:

2) Если тип назначения является беззнаковым, результирующее значение является наименьшим целым числом без знака, соответствующим исходному целому числу (по модулю 2 n, где n - число бит, используемых для представления типа без знака). [Примечание: в представлении дополнения до двух это преобразование является концептуальным, и в битовой комбинации нет изменений (если нет усечения). ]

3) Если тип назначения подписан, значение не изменяется, если оно может быть представлено в типе назначения (и ширине битового поля); в противном случае значение определяется реализацией.

(Акцент мой.)

1 голос
/ 24 марта 2009

Я не уверен, но я думаю, что вы, вероятно, ошибаетесь из-за ошибки.

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

for(unsigned int i=a.length()-1; i+1 >= 1; --i)   // As written

for (unsigned int i = a.length()-1; i >= 0; --i) // Noting 1 appears twice

for (unsigned int i = a.length()-1; ; --i)   // Because i >= 0 at all times

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

Возможно, вам было бы лучше использовать более стандартную формулировку цикла:

for (unsigned i = a.length()-1; i-- > 0; )
0 голосов
/ 24 марта 2009

Да, я только что проверил это на Visual Studio 2005, оно определенно ведет себя по-разному в Debug и Release. Интересно, исправит ли это 2008 год?

Интересно, что он жаловался на ваше неявное приведение от size_t (результат .length) к unsigned int, но без проблем генерирует плохой код.

...