V205. Explicit conversion of pointer type to 32-bit integer type.
Анализатор обнаружил явное приведение указателя к 32-битному целочисленному типу. Ранее подобную ситуацию можно было выявить с помощью диагностического правила V202. Однако явное приведение указателя к типу int
, гораздо опаснее, чем приведение intptr_t
к типу int
. Поэтому было создано отдельное правило для поиска явного приведения типов при работе с указателями.
Пример некорректного кода.
int n;
float *ptr;
...
n = (int)ptr;
В 64-битной программе тип int
имеет размер 4 байта и не может вместить в себя указатель размером 8 байт. Приведение типа, как показано в примере, практически всегда свидетельствует о наличии ошибки.
Отметим, что такие ошибки очень неприятны тем, что могут не сразу проявить себя. Программа может хранить указатели в 32-битных переменных и некоторое время корректно работать, пока все создаваемые в программе объекты располагаются в младших адресах оперативной памяти.
Если по каким-то причинам необходимо хранить указатель в переменной целочисленного типа, то для этого следует использовать memsize-типы данных. Например: size_t
, ptrdiff_t
, intptr_t
, uintptr_t
.
Пример корректного кода:
intptr_t n;
float *ptr;
...
n = (intptr_t)ptr;
Возможна специфическая ситуация, когда указатель допустимо хранить в 32-битных типах. Речь идет о дескрипторах (handles), которые используются в Windows для работы с различными системными объектами. Примеры таких типов: HANDLE
, HWND
, HMENU
, HPALETTE
, HBITMAP
и так далее. По сути, эти типы являются указателями. Например, HANDLE объявляется в заголовочных файлах как typedef void *HANDLE;
.
Хотя дескрипторы являются 64-битными указателями, для большей совместимости (например, для возможности взаимодействия между 32-битынми и 64-битными процессами) в них используется только младшие 32-бита. Подробнее об этом можно прочитать здесь: "Microsoft Interface Definition Language (MIDL): 64-Bit Porting Guide" (USER and GDI handles are sign extended 32b values).
Такие указатели можно хранить в 32-битных типах данных (например, int
, DWORD
). Для преобразования таких указателей к 32-битным типам и обратно используются специальные функции:
void * Handle64ToHandle( const void * POINTER_64 h )
void * POINTER_64 HandleToHandle64( const void *h )
long HandleToLong ( const void *h )
unsigned long HandleToUlong ( const void *h )
void * IntToPtr ( const int i )
void * LongToHandle ( const long h )
void * LongToPtr ( const long l )
void * Ptr64ToPtr ( const void * POINTER_64 p )
int PtrToInt ( const void *p )
long PtrToLong ( const void *p )
void * POINTER_64 PtrToPtr64 ( const void *p )
short PtrToShort ( const void *p )
unsigned int PtrToUint ( const void *p )
unsigned long PtrToUlong ( const void *p )
unsigned short PtrToUshort ( const void *p )
void * UIntToPtr ( const unsigned int ui )
void * ULongToPtr ( const unsigned long ul )
Рассмотрим пример:
HANDLE h = Get();
UINT uId = (UINT)h;
Анализатор не выдаёт здесь сообщение, хотя HANDLE — это указатель. Его значения всегда помещаются в 32-бита. Следует учитывать, что невалидные дескрипторы (handles) объявлены следующим образом:
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
Поэтому некорректно ниже будет написать так:
if (HANDLE(uID) == INVALID_HANDLE_VALUE)
Т.к. переменная uID
беззнаковая, то значение указателя будет равно не 0xFFFFFFFFFFFFFFFF, а 0x00000000FFFFFFFF.
Для подозрительной проверки, где unsigned будет превращаться в указатель, будет выдано предупреждение V204.