Анализатор обнаружил явное приведение 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 )