﻿# V220\. Suspicious sequence of types castings: memsize \-\> 32\-bit integer \-\> memsize\.


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

Предупреждение информирует о наличии странной последовательности приведений типа\. Memsize\-тип явно приводится к 32\-битному целочисленному типу\. А затем тут же вновь явно или неявно приводится к memsize\-типу\. Такая последовательность приведений приводит к потере значений старших бит\. Как правило, это свидетельствует о наличии серьезной ошибки\.

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

```cpp
char *p1;
char *p2;
ptrdiff_t n;
...
n = int(p1 - p2);
```

Здесь присутствует лишнее приведение к типу 'int'\. Оно не нужно и может послужить причиной сбоя, если в 64\-битной программе указатели p1 и p2 будут отстоять друг от друга больше чем на INT\_MAX элементов\.

Корректный вариант кода:

```cpp
char *p1;
char *p2;
ptrdiff_t n;
...
n = p1 - p2;
```

Рассмотрим другой пример:

```cpp
BOOL SetItemData(int nItem, DWORD_PTR dwData);
...
CItemData *pData = new CItemData;
...
CListCtrl::SetItemData(nItem, (DWORD)pData);
```

Этот код приведёт к ошибке, если объект типа CItemData будет создан за пределами четырех младших гигабайт памяти\. Корректный вариант кода:

```cpp
BOOL SetItemData(int nItem, DWORD_PTR dwData);
...
CItemData *pData = new CItemData;
...
CListCtrl::SetItemData(nItem, (DWORD_PTR)pData);
```

Следует учитывать, что анализатор не выдаёт предупреждение, если конвертации подвергается такие типы данных, как HANDLE, HWND, HCURSOR и так далее\. Хотя, по сути, это указатели \(void \*\), их значения всегда вмещаются в младшие 32\-бита\. Это сделано специально, чтобы эти дескрипторы \(handles\) можно было передавать между 32\-битными и 64\-битными процессами\. Подробнее: [Как корректно привести указатель к int в 64\-битной программе?](https://pvs-studio.ru/ru/blog/posts/cpp/k0005/)

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

```cpp
typedef void * HANDLE;
HANDLE GetHandle(DWORD nStdHandle);
int _open_osfhandle(intptr_t _OSFileHandle, int _Flags);
....
int fh = _open_osfhandle((int)GetHandle(sh), 0);
```

Здесь имеет место преобразование вида:

HANDLE \-\> int \-\> intptr\_t

Т\.е\. вначале указатель превращается в 32\-битный тип 'int', а замет расширяется до 'intptr\_t'\. Это не очень красиво\. Лучше было бы написать "\(intptr\_t\)GetHandle\(STD\_OUTPUT\_HANDLE\)"\. Однако, никакой ошибки здесь и нет, так как значения типа HANDLE помещаются в 'int'\. Поэтому анализатор промолчит\. 

Если будет написано:

```cpp
int fh = _open_osfhandle((unsigned)GetHandle(sh), 0);
```

То анализатор выдаст предупреждение\. Смешивание знаковых и беззнаковых типов всё портит\. Представим, что GetHandle\(\) вернёт INVALID\_HANDLE\_VALUE\. Это значение задано в системных заголовочных файлах следующим образом\.

```cpp
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
```

Посмотрим, что получитcя при преобразовании \(intptr\_t\)\(unsigned\)\(\(HANDLE\)\(LONG\_PTR\)\-1\):

\-1 \-\> 0xffffffffffffffff \-\> HANDLE \-\> 0xffffffffu \-\> 0x00000000fffffffff

Значение \-1 превратилось в 4294967295\. Это может быть не учтено программистом и программа может работать некорректно, если функция GetHandle\(\) вернёт INVALID\_HANDLE\_VALUE\. Поэтому, для второго случая анализатор выдаст предупреждение\.