>
>
>
How to correctly cast a pointer to int …

Andrey Karpov
Articles: 674

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 cannot be put into the int type, which remains 32-bit in almost all 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 the LP64 and LLP64 data models, where int is 32-bit.

Putting a 64-bit pointer into a 32-bit variable causes the cutting of high-order bits, and therefore incorrect program behavior. 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 the 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 could potentially crash at any moment (see an example).

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

However, there are specific cases 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 to 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. However, in many old projects which contain third-party libraries, many compiler-generated warnings are disabled, so, the probability of you missing such errors is greatly increased. 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