V220. Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize.
Предупреждение информирует о наличии странной последовательности приведений типа. Memsize-тип явно приводится к 32-битному целочисленному типу. А затем тут же вновь явно или неявно приводится к memsize-типу. Такая последовательность приведений приводит к потере значений старших бит. Как правило, это свидетельствует о наличии серьезной ошибки.
Рассмотрим пример:
char *p1;
char *p2;
ptrdiff_t n;
...
n = int(p1 - p2);
Здесь присутствует лишнее приведение к типу 'int'. Оно не нужно и может послужить причиной сбоя, если в 64-битной программе указатели p1 и p2 будут отстоять друг от друга больше чем на INT_MAX элементов.
Корректный вариант кода:
char *p1;
char *p2;
ptrdiff_t n;
...
n = p1 - p2;
Рассмотрим другой пример:
BOOL SetItemData(int nItem, DWORD_PTR dwData);
...
CItemData *pData = new CItemData;
...
CListCtrl::SetItemData(nItem, (DWORD)pData);
Этот код приведёт к ошибке, если объект типа CItemData будет создан за пределами четырех младших гигабайт памяти. Корректный вариант кода:
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-битной программе?
Рассмотрим пример:
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'. Поэтому анализатор промолчит.
Если будет написано:
int fh = _open_osfhandle((unsigned)GetHandle(sh), 0);
То анализатор выдаст предупреждение. Смешивание знаковых и беззнаковых типов всё портит. Представим, что GetHandle() вернёт INVALID_HANDLE_VALUE. Это значение задано в системных заголовочных файлах следующим образом.
#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. Поэтому, для второго случая анализатор выдаст предупреждение.