Sergey Vasiliev
3 min readAug 11, 2015

How to correctly cast a pointer to int in a 64-bit application?

The most general answer is — in no way.

In 64-bit programs, the size of the pointer is 64 bits and it cannot be put into the int type that remains 32-bit almost in all the systems. The only exception is exotic systems with the SILP64 data model where the size of int is also 64 bits. The most common operating systems (Windows, Linux, MacOS, …) use theLP64 and LLP64 data models where int is 32-bit.

Putting a 64-bit pointer into a 32-bit variable causes cutting of high-order bitsand therefore incorrect program behavior. The code like this is invalid:

void *ptr = ...;
int x = (int)ptr;
...
ptr = (void *)x;

This code is also dangerous because it hides an error that might reveal itself only in a long term. As long as pointers refer to objects created inside low-order bits of memory addresses, the program will work correctly and perhaps for a long time. But this is a deceptive impression of an operable application and it might crash at any moment (see an example).

If the programmer needs for some reason to store pointers in integer types, he may use memsize-typesfor that — for instance, intptr_t, size_t, INT_PTR, etc.

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 )

Note that simple errors of casting pointers to 32-bit types are well diagnosed by the Visual C++ compiler. But in many old projects that contain third-party libraries, many compiler-generated warnings are disabled, so the probability for you to miss such errors is greatly increased because of this. In the situation described, it is reasonable to use a specialized tool for providing portability of code to the 64-bit platform — for instance, the Viva64 static code analyzer developed by our company.

References

Article republished by the author’s permission.

Sergey Vasiliev
Sergey Vasiliev

Written by Sergey Vasiliev

Head of DevRel at PVS-Studio LLC

No responses yet