V205. Explicit conversion of pointer type to 32-bit integer type.
This warning informs you about an explicit conversion of a pointer type to a 32-bit integer type. We used the V202 diagnostic rule before to diagnose this situation. But explicit conversion of a pointer to the 'int' type is much more dangerous than conversion of 'intptr_t' to 'int'. That is why we created a separate rule to search for explicit type conversions when handling pointers.
Here is a sample of incorrect code.
int n; float *ptr; ... n = (int)ptr;
The 'int' type's size is 4 bytes in a 64-bit program, so it cannot store a pointer whose size is 8 bytes. Type conversion like in the sample above usually signals an error.
What is very unpleasant about such errors is that they can hide for a long time before you reveal them. A program might store pointers in 32-bit variables and work correctly for some time as long as all the objects created in the program are located in low-order addresses of memory.
If you need to store a pointer in an integer variable for some reason, you'd better use memsize-types. For instance: size_t, ptrdiff_t, intptr_t, uintptr_t.
This is the correct code:
intptr_t n; float *ptr; ... n = (intptr_t)ptr;
However, there is a specific case when you may store a pointer in 32-bit types. I am speaking about handles which are used in Windows to work with various system objects. Here are examples of such types: HANDLE, HWND, HMENU, HPALETTE, HBITMAP, etc. Actually these types are pointers. For instance, HANDLE is defined in header files as "typedef void *HANDLE;".
Although handles are 64-bit pointers, only the less significant 32 bits are employed in them for the purpose of better compatibility (for example, to enable 32-bit and 64-bit processes interact with each other). For details, see "Microsoft Interface Definition Language (MIDL): 64-Bit Porting Guide" (USER and GDI handles are sign extended 32b values).
Such pointers can be stored in 32-bit data types (for instance, int, DWORD). To cast such pointers to 32-bit types and vice versa special functions are used:
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 )
Let's take a look at the following example:
HANDLE h = Get(); UINT uId = (UINT)h;
The analyzer does not generate the message here, though HANDLE is nothing but a pointer. Values of this pointer always fit into 32 bits. Just make sure you take care when working with them in future. Keep in mind that non-valid handles are declared in the following way:
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
That's why it would be incorrect to write the next line like this:
if (HANDLE(uID) == INVALID_HANDLE_VALUE)
Since the 'uID' variable is unsigned, the pointer's value will equal 0x00000000FFFFFFFF, not 0xFFFFFFFFFFFFFFFF.
The analyzer will generate the V204 warning for a suspicious check when unsigned turns into a pointer.
Additional materials on this topic:
- Knowledge Base. "How to correctly cast a pointer to int in a 64-bit application?"