V204. Explicit conversion from 32-bit integer type to pointer type.
This warning informs you about an explicit conversion of a 32-bit integer type to a pointer type. We used the V201 diagnostic rule before to diagnose this situation. But explicit conversion of the 'int' type to pointer is much more dangerous than conversion of 'int' to 'intptr_t'. 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; ... ptr = (float *)(n);
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; ... ptr = (float *)(n);
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 )
Additional materials on this topic:
- Knowledge Base. "How to correctly cast a pointer to int in a 64-bit application?"