Анализатор обнаружил возможное переполнение знаковой переменной в цикле. Переполнение знаковых переменных приводит к неопределённому поведению.
Пример:
int checksum = 0;
for (....) {
checksum += ....;
}
Перед нами абстрактный алгоритм подсчёта контрольной суммы. Алгоритм подразумевает возможность переполнения переменной 'checksum'. Однако переменная имеет знаковый тип, а, следовательно, её переполнение приводит к неопределённому поведению. Код некорректен и должен быть модифицирован.
Следует использовать беззнаковые типы, семантика переполнения которых определена.
Корректный код:
unsigned checksum = 0;
for (....) {
checksum += ...
}
Некоторые программисты считают, что в переполнении знаковых переменных ничего страшного нет и они могут предсказать, как программа будет работать. Это не так. Может происходить всё что угодно.
Давайте рассмотрим, как могут проявляться ошибки этого типа на практике. Разработчик на форуме жалуется, что GCC глючит и неправильно компилирует его код в режиме оптимизации. Он приводит код следующей функции для подсчёта хеша строки:
int foo(const unsigned char *s)
{
int r = 0;
while(*s) {
r += ((r * 20891 + *s *200) | *s ^ 4 | *s ^ 3) ^ (r >> 1);
s++;
}
return r & 0x7fffffff;
}
Претензия разработчика в том, что компилятор не генерирует код для оператора побитового И (&). Из-за этого функция возвращает отрицательные значения, хотя не должна.
Разработчик считает, что это глюк в компиляторе. Но это не так, неправ программист, который написал такой код. Функция работает неправильно из-за того, что в ней возникает неопределённое поведение.
Компилятор видит, что в переменной 'r' считается некоторая сумма. Переполнения переменной 'r' произойти не должно. Иначе это неопределённое поведение, которое компилятор никак не должен рассматривать и учитывать. Итак, компилятор считает, что раз переменная 'r' после окончания цикла не может быть отрицательной, то операция 'r & 0x7fffffff' для сброса знакового бита является лишней и компилятор просто возвращает из функции значение переменной 'r'.
Диагностика V5010 как раз и предназначена для выявления подобных ошибок. Чтобы исправить код, достаточно считать хеш, используя для этого беззнаковую переменную.
Исправленный вариант кода:
int foo(const unsigned char *s)
{
unsigned r = 0;
while(*s) {
r += ((r * 20891 + *s *200) | *s ^ 4 | *s ^ 3) ^ (r >> 1);
s++;
}
return (int)(r & 0x7fffffff);
}
Дополнительные ссылки:
Данная диагностика классифицируется как:
|