Для получения триального ключа
заполните форму ниже
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Мне интересно попробовать плагин на:
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
V1085. Negative value is implicitly con…
Проверка проектов
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Диагностика микро-оптимизаций (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Реализовано по запросам пользователей (C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C#)
Проблемы при работе анализатора кода
Дополнительная информация
Оглавление

V1085. Negative value is implicitly converted to unsigned integer type in arithmetic expression.

20 Май 2022

Анализатор обнаружил ситуацию, в которой в арифметическом выражении происходит конвертация отрицательного числа к беззнаковому типу. Согласно правилам неявного преобразования C/C++, знаковое число той же размерности, что и беззнаковое, превращается в беззнаковое. При приведении отрицательного числа к беззнаковому типу происходит его "оборачивание" по модулю '(2 ^ n) + 1', где n – количество бит в числе. Такая ситуация не приводит к неопределённому поведению, но может привести к неожиданным результатам.

Рассмотрим пример:

void foo()
{
  char *p = (char *) 64;
  int32_t a = -8;
  uint32_t b = 8;
  p = p + a * b;
}

На 32-битной системе в указателе получается 0x0. На 64-битной – 0x0000'0001'0000'0000, что может быть неожиданно для программиста. Давайте разберёмся, почему так происходит.

Переменная 'a' имеет знаковый тип 'int32_t'. Это означает, что её размер — 4 байта и она может принимать значения в диапазоне от -2'147'483'648 до 2'147'483'647. Переменная 'b' имеет тип 'uint32_t'. Она также имеет размер в 4 байта, но, в отличие от переменной 'a', может принимать значения в диапазоне от 0 до 4'294'967'295. Так происходит потому, что старший бит в знаковом числе зарезервирован под знак. Из-за этого ограничения максимальное значение знакового числа вдвое меньше, чем у беззнакового.

По правилам языка C++, если в бинарной операции операнды имеют типы с одинаковым рангом и один из операндов имеет знаковый тип, а другой — беззнаковый, то операнд, который имеет знаковый тип, неявно приводится к беззнаковому.

В выражении 'a * b' типы операндов ('int32_t' и 'uint32_t') имеют одинаковый ранг. Следовательно, операнд 'a', который хранит в себе значение '-8' неявно приводится к беззнаковому типу 'uint32_t'. В результате такого приведения его значение становится равным 4'294'967'288. Далее происходит умножение на переменную 'b', которая хранит значение '8'. Полученный результат, который равен 34'359'738'304, выходит за рамки диапазона возможных значений переменной типа 'uint32_t' и будет обернут по модулю '2 ^ 32'. Таким образом, результат выражения 'a * b' будет равен 34'359'738'304 % 4'294'967'296 = 4'294'967'232.

У оператора сложения 'p + a * b' типы операндов 'char *' и 'uint32_t' соответственно. Согласно стандарту C++, результирующий тип будет 'char *', а результат – сумма левого и правого операндов. При сложении 64 и 4'294'967'232 результат равен 4'294'967'296.

На 32-битной платформе размер указателя равен 4 байтам. Следовательно, его максимальное значение равно 4'294'967'295. Так как число 4'294'967'296 больше, то результат оборачивается по модулю '2 ^ 32', как и в предыдущей операции сложения, и будет равен 4'294'967'296 % 4'294'967'296 = 0. В итоге результат выражения 'p + a * b' равен нулю.

На 64-битной платформе размер указателя равен 8 байтам. И в отличие от 32-битной платформы его максимальное значение куда больше, чем 4'294'967'296. Так как оборачивания не будет происходить, то результат выражения 'p + a * b' равен 4'294'967'296 в десятичной системе или 0x0000'0001'0000'0000 в шестнадцатеричной.

Исправить приведённый выше пример можно, использовав знаковые типы для вычислений:

void foo()
{
  char *p = (char *) 64;
  int32_t a = -8;
  uint32_t b = 8;
  p = p + a * static_cast<int32_t>(b);
}

Диагностика не будет сообщать обо всех конвертациях знаковых типов к беззнаковым. Это будет происходить только в тех выражениях, результат вычислений которых будет отличаться от такового при использовании только знаковых типов. Рассмотрим пример:

void foo()
{
  unsigned num = 1;
  unsigned res1 = num + (-1); // ok
  unsigned res5 = num + (-2); //+V1085

  unsigned res2 = num - (-1); // ok

  unsigned res3 = num * (-1); //+V1085


  unsigned res4 = num / (-1); //+V1085
  unsigned res6 = num / (-2); // ok 

  unsigned num2 = 2;

  unsigned res7 = num2 / (-1); //+V1085
}

В строчках, отмеченных комментарием 'ok', не будет предупреждения V1085. Приведём результаты вычислений каждого выражения со знаковыми и беззнаковыми вариантами:

num  +   (signed)(-1) => 1 + (-1) => 0 
num  + (unsigned)(-1) => 1 + 4294967295 = 0 

num  +   (signed)(-2) => 1 + (-2) => -1
num  + (unsigned)(-2) => 1 + 4294967294 = 4294967295

num  -   (signed)(-1) => 1 – (-1) => 2
num  - (unsigned)(-1) => 1 – (4294967295) => 2

num  *   (signed)(-1) => 1 * (-1) => -1
num  * (unsigned)(-1) => 1 * (4294967295) => 4294967295

num  /   (signed)(-1) => 1 / (-1) => -1
num  / (unsigned)(-1) => 1 / 4294967295 => 0

num  /   (signed)(-2) => 1 / (-2) => 0
num  / (unsigned)(-2) => 1 / 4294967294 => 0

num2 /   (signed)(-2) => 2 / (-2) => -1
num2 / (unsigned)(-2) => 2 / 4294967294 => 0

В тех местах, где результаты совпадают, предупреждение будет отсутствовать.

Примечание. Рассмотренные проблемы пересекаются с темой переноса приложений с 32-битных на 64-битные системы. См. статью: "Коллекция примеров 64-битных ошибок в реальных программах".

Данная диагностика классифицируется как:

Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо