V204. Explicit conversion from 32-bit integer type to pointer type.
Анализатор обнаружил явное приведение 32-битного целочисленного типа к указателю. Ранее подобную ситуацию можно было выявить с помощью диагностического правила V201. Однако явное приведение типа int
к указателю гораздо опаснее, чем приведение int
к типу intptr_t
. Поэтому было создано отдельное правило для поиска явного приведения типов при работе с указателями.
Пример некорректного кода.
int n;
float *ptr;
...
ptr = (float *)(n);
В 64-битной программе тип int
имеет размер 4 байта и не может вместить в себя указатель размером 8 байт. Приведение типа, как показано в примере, практически всегда свидетельствует о наличии ошибки.
Отметим, что такие ошибки очень неприятны тем, что могут не сразу проявить себя. Программа может хранить указатели в 32-битных переменных и некоторое время корректно работать, пока все создаваемые в программе объекты располагаются в младших адресах оперативной памяти.
Если по каким-то причинам необходимо хранить указатель в переменной целочисленного типа, то для этого следует использовать memsize-типы данных. Например: size_t
, ptrdiff_t
, intptr_t
, uintptr_t
.
Пример корректного кода:
intptr_t n;
float *ptr;
...
ptr = (float *)(n);
Возможна специфическая ситуация, когда указатель допустимо хранить в 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 )