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).
Comments
- Anonymous
October 31, 2005
> If you're like me, your first reaction might
> be, "that's dumb. pointers are unsigned."
You're right, and when you dug for reasons to break that, the reasons you came up with aren't as good as you think they are. Back in the Jurassic era when C didn't have "unsigned", the way to persuade the compiler to do unsigned arithmetic was to declare a variable as type char* instead of int. Pointers have been unsigned in C for more years than Microsoft has existed. You're breaking a lot more backwards compatibility than you're buying.
> 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,
That needs to be on by default. Even this tiny little bit of a notice that programmers need to do workarounds, whose idea was it to hide this notice by default? Hotfix. Now. And downloadable, not requiring customers to pay to open a support incident. - Anonymous
October 31, 2005
Hi Norman,
I definitely appreciate your thoughts on this, although I'm not sure this meets the bar for a hotfix. The number of customers affected is very low because it's quite uncommon to use type* __ptr64 in 32-bit code (or, conversely, type* __ptr32 in 64-bit code). It's more an interesting bit of curio than a common real world scenario mosts folks need be concerned about. I think the NT kernel is by far the biggest consumer of this feature, and a handful of companies producing drivers use it as well.
-steve - Anonymous
October 31, 2005
BTW, I modified the text to make it clear I'm referring to the 32-bit compiler.
I should also mention that __ptr64 is non-standard, so there isn't any backward compatibility being broken here. - Anonymous
November 02, 2005
Your base note mentions at least one person who uses type qualifier __ptr64. Furthermore type qualifier __ptr64 was created in the first place in order to be used by at least one person. You know that the effect is counterintuitive, you know that a warning is advisable, and you discovered that the warning is set not to display by default when it's needed. You know that the default needs to be changed to display the warning.
You're right that there's no old code needing backwards compatibility on this one, it's only human brains that need it, and the warning is needed for human brains.