When is 4 gig not 4 gig?

Interesting problem came up last week.  Let's say you compile the following code with VC++ targeting 32-bit:

void *__ptr64 a;
void * b = (void*)0xFFFFFFFF;
a = b;

Care to guess what the final value of a is?  Conventional wisdom might dictate that the value should be 0x00000000FFFFFFFF.  After all, a pointer is an unsigned entity, and a pointer that points to 4 gig in the 32-bit world should point to 4 gig in the 64-bit world, right? Not quite.  It turns out, the conversion is sign extended, so the final value of a is actually 0xFFFFFFFFFFFFFFFF.

If you're like me, your first reaction might be, "that's dumb.  pointers are unsigned." However, when I did some digging it turns out that there are several good reasons for the conversion to be signed.  Some of the reasons deal with special treatment of the most significant bit in certain circumstances (e.g. as a supervisor bit) that needs to be maintained.  But the most compelling reason is the special treatment of (pointer)(-1) in the Win32 API.  As just one example, HWND_TOPMOST is defined as ((HWND)-1).  Simply put, creating a situation where a pointer value of -1 was no longer -1 after conversion to type* __ptr64 would break a lot more code in more subtle ways than simply requiring the developer to indicate their intention clearly during the conversion, as can be done using PtrToPtr64():

void *__ptr64 a;
void * b = (void*)0xFFFFFFFF;
a = PtrToPtr64(b);

In the above example, the final value of a is now 0x00000000FFFFFFFF.

BTW, there is a compiler warning, C4826, that will provide diagnostic information if you blindly assign a type* to a type* __ptr64.  It's off by default, but you can enable it using, for example:

#pragma warning(default: 4826).