﻿# V101\. Implicit assignment type conversion to memsize type\.


> Это правило входит в группу "Диагностика 64\\\-битных ошибок"\\\. Разработка правил этой группы больше не ведётся, и в будущем они могут быть отключены\\\. Если вы используете эти правила, \[свяжитесь с нашей поддержкой\]\(https://pvs\-studio\.ru/ru/about\-feedback/\) — мы поможем найти замену или предложим альтернативное решение\\\.

Анализатор обнаружил потенциально возможную ошибку, связанную с неявным приведением типа при выполнении оператора присваивания "\="\. Ошибка может заключаться в некорректном вычислении значения выражения, стоящего справа от оператора присваивания "\="\.

Пример кода, вызывающего предупреждение: 

```cpp
size_t a;
unsigned b;
...
a = b; // V101
```

Сама по себе операция приведения 32\-битного типа к [memsize\-типу](https://pvs-studio.ru/ru/blog/terms/0030/) безопасна, поскольку не происходит потери данных\. Например, в переменной типа size\_t всегда можно сохранить значение переменной типа unsigned\. Но наличие такого приведения типа может указывать на скрытую ошибку, допущенную ранее\.

Первой причиной возникновения ошибки на 64\-битной системе может служить изменение процесса вычисления выражений\. Рассмотрим  пример:

```cpp
unsigned a = 10;
int b = -11;
ptrdiff_t c = a + b; //V101
cout << c << endl;
```

Данный код на 32\-битной системе распечатает значение \-1, а на 64\-битной системе 4294967295\. Данное поведение полностью согласуется с правилами приведения типов в языке Си\+\+, но скорее всего приведет к ошибке в реальном коде\.

Поясним приведенный пример\. Согласно правилам языка Си\+\+ выражение a\+b имеет тип unsigned и содержит значение 0xFFFFFFFFu\. На 32\-битной системе тип ptrdiff\_t представляет собой знаковый 32\-битный тип\. После присвоения 32\-битной знаковой переменной значения 0xFFFFFFFFu она будет содержать значение \-1\. На 64\-битной системе тип ptrdiff\_t представляет собой знаковый 64\-битный тип\. Это означает, что число 0xFFFFFFFFu будет представлено как оно есть\. То есть значение переменной после присваивания будет равняться 4294967295\.

Ошибку можно исправить, исключив смешанное использования memsize и не memsize\-типов в одном выражении\. Пример исправления кода:

```cpp
size_t a = 10;
ptrdiff_t b = -11;
ptrdiff_t c = a + b;
cout << c << endl;
```

Еще более корректным способом исправления будет отказ от смешенного использования знаковых и беззнаковых типов данных\.

Второй причиной ошибки может стать переполнение, возникающее в 32\-битных типах данных\. В этом случае ошибка может содержаться раньше самого оператора присваивания, но обнаружить ее можно только косвенно\. Такие ошибки часто встречаются в коде, выделяющим большие объемы памяти\. Рассмотрим пример:

```cpp
unsigned Width  = 1800;
unsigned Height = 1800;
unsigned Depth  = 1800;
// Real error is here
unsigned CellCount = Width * Height * Depth;
// Here we get a diagnostic message V101
size_t ArraySize = CellCount * sizeof(char);
cout << ArraySize << endl;
void *Array = malloc(ArraySize);
```

Предположим, что на 64\-битной системе мы решили обрабатывать массивы данных более 4 гигабайт\. В этом случае показанный код приведет к выделению ошибочного объема памяти\. Программист планирует выделить 5832000000 байт оперативной памяти, а вместо этого получит в свое распоряжение только 1537032704\. Это происходит из\-за переполнения при вычислении выражения Width \* Height \* Depth\. К сожалению мы не можем диагностировать ошибку в строке с этим выражением, но мы можем косвенно указать на наличие ошибки, обнаружив приведение типа в строке: 

```cpp
size_t ArraySize = CellCount * sizeof(char); //V101
```

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

```cpp
size_t CellCount = Width * Height * Depth;
```

Здесь мы по\-прежнему имеем переполнение\. Приведем два примера корректного исправления кода:

// 1\) 

```cpp
unsigned Width  = 1800;
unsigned Height = 1800;
unsigned Depth  = 1800;
size_t CellCount =
  static_cast<size_t>(Width) *
  static_cast<size_t>(Height) *
  static_cast<size_t>(Depth);
```

// 2\) 

```cpp
size_t Width  = 1800;
size_t Height = 1800;
size_t Depth  = 1800;
size_t CellCount = Width * Height * Depth;
```

Следует учитывать, что ошибка может находиться не просто выше, а вообще в другом модуле\. Приведем соответствующий пример\. В нем ошибка заключается в некорректном вычислении индекса, если размер массива превысит 4 Гб\.

Пусть приложение использует большой одномерный массив, и функция CalcIndex позволяет адресоваться к этому массиву как к двумерному массиву\.

```cpp
extern unsigned ArrayWidth;
unsigned CalcIndex(unsigned x, unsigned y) {
  return x + y * ArrayWidth;
}
   ...
const size_t index = CalcIndex(x, y); //V101
```

Анализатор сообщит о проблеме в строке: const size\_t index \= CalcIndex\(x, y\)\. Но ошибка заключается в неправильной реализации функции CalcIndex\. Если взять функцию CalcIndex отдельно, то она абсолютно корректна\. Типом выходного и входных значений является unsigned\. Вычисления также происходят с участием только типов unsigned\. Никаких явных или неявных приведений типов нет, и анализатор не имеет возможности распознать логическую проблему, связанную с функцией CalcIndex\. Ошибка заключается в том, что неверно выбран результат, возвращаемый функцией, а возможно и входных значений\. Результат функции должен иметь тип memsize\.

К счастью анализатор смог обнаружить неявное приведение результата функции CalcIndex к типу size\_t\. Это позволяет вам проанализировать ситуацию и внести в программу необходимые изменения\. Исправление ошибки, например, может выглядеть следующим образом:

```cpp
extern size_t ArrayWidth;
size_t CalcIndex(size_t x, size_t y) {
  return x + y * ArrayWidth;
}
   ...
const size_t index = CalcIndex(x, y);
```

Если вы уверены, что код верен и размер массива никогда не приблизится к границе в 4Гб, вы можете подавить предупреждение анализатора, используя [явное приведение типа](https://pvs-studio.ru/ru/blog/terms/0015/):

```cpp
extern unsigned ArrayWidth;
unsigned CalcIndex(unsigned x, unsigned y) {
  return x + y * ArrayWidth;
}
...
const size_t index = static_cast<size_t>(CalcIndex(x, y));
```

В ряде случаев и сам анализатор может понять, что переполнение при вычислении невозможно и предупреждение не будет распечатано\.

Рассмотрим последний пример, связанный с некорректными операциями сдвига\.

```cpp
ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {
  ptrdiff_t mask = 1 << bitNum; //V101
  return value | mask;
}
```

Выражение " mask \= 1 << bitNum " является опасным, так как данный код не может выставить в единицы старшие разряды 64\-битной переменной mask\. Если попробовать использовать функцию SetBitN для выставления, например, 33\-го бита, то произойдет переполнение при выполнении операции сдвига и желаемый результат не будет достигнут\.

Дополнительные материалы по данной теме:

1. 64\-битные уроки\. Урок 11\. Паттерн 3\. [Операции сдвига](https://pvs-studio.ru/ru/blog/lessons/0011/)\.
1. 64\-битные уроки\. Урок 17\. Паттерн 9\. [Смешанная арифметика](https://pvs-studio.ru/ru/blog/lessons/0017/)\.