指针使用规则
移植代码以同时编译 32 位和 64 位 Microsoft Windows 非常简单。 只需遵循几个有关转换指针的简单规则,并在代码中使用新的数据类型。 指针操作的规则如下所示。
不要将指针强制转换为 int、 long、 ULONG 或 DWORD。
如果必须强制转换指针以测试某些位、设置或清除位,或者以其他方式操作其内容,请使用 UINT_PTR 或 INT_PTR 类型。 这些类型是可缩放到 32 位和 64 位 Windows (指针大小的整型类型,例如,32 位 Windows 的 ULONG 和 64 位 Windows) 的 _int64。 例如,假设要移植以下代码:
ImageBase = (PVOID)((ULONG)ImageBase | 1);
作为移植过程的一部分,你将更改代码,如下所示:
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);
在适当 情况下使用UINT_PTR 和 INT_PTR (如果不确定是否需要它们,则使用它们不会造成损害,以防) 。 不要将指针强制转换为 ULONG、 LONG、 INT、 UINT 或 DWORD 类型。
请注意, HANDLE 定义为 void*,因此将 HANDLE 值类型化为 ULONG 值以测试、设置或清除低阶 2 位在 64 位 Windows 上是一个错误。
使用 PtrToLong 或 PtrToUlong 函数截断指针。
如果必须截断指向 32 位值的指针,请使用 Basetsd.h) 中定义的 PtrToLong 或 PtrToUlong 函数 (。 这些函数在调用期间禁用指针截断警告。
请谨慎使用这些函数。 使用这些函数之一转换指针变量后,再也不要将其用作指针。 这些函数截断地址的上限 32 位,通常需要这些地址来访问最初由指针引用的内存。 如果不仔细考虑这些函数,将导致代码脆弱。
在可能编译为 64 位代码的代码中使用POINTER_32值时要小心。 当指针在 64 位代码中分配给本机指针时,编译器将对指针进行签名扩展,而不是对指针进行零扩展。
在可能编译为 32 位代码的代码中使用POINTER_64值时要小心。 编译器将使用 32 位代码对指针进行签名扩展,而不是对指针进行零扩展。
使用 OUT 参数时要小心。
例如,假设有一个定义如下的函数:
void func( OUT PULONG *PointerToUlong );
请勿按如下所示调用此函数。
ULONG ul; PULONG lp; func((PULONG *)&ul); lp = (PULONG)ul;
请改用以下调用。
PULONG lp; func(&lp);
将 ul 类型转换 &到 PULONG* 可防止编译器错误,但函数会将 64 位指针值写入 ul 的 &内存中。 此代码适用于 32 位 Windows,但会导致 64 位 Windows 上的数据损坏,并且会是微妙的、难以找到的损坏。 底线:不要使用 C 代码玩技巧 - 简单直接更好。
使用多态接口时要小心。
不要创建接受多态数据的 DWORD 参数的函数。 如果数据可以是指针或整型值,请使用 UINT_PTR 或 PVOID 类型。
例如,不要创建接受类型为 DWORD 值的异常参数数组的函数。 数组应该是 DWORD_PTR 值的数组。 因此,数组元素可以保存地址或 32 位整型值。 (一般规则是,如果原始类型为 DWORD 并且需要指针宽度,请将其转换为 DWORD_PTR 值。这就是存在相应的指针精度类型的原因。) 如果代码以多态方式使用 DWORD、 ULONG 或其他 32 位类型 (,即,你确实希望参数或结构成员持有地址) ,请使用 UINT_PTR 代替当前类型。
使用新的窗口类函数。
如果具有包含指针的窗口或类私有数据,则代码将需要使用以下新函数:
这些函数可以在 32 位和 64 位 Windows 上使用,但在 64 位 Windows 上是必需的。 立即使用这些函数为转换做好准备。
此外,必须在 64 位 Windows 上使用新函数访问类私有数据中的指针或句柄。 为了帮助你查找这些情况,在 64 位编译期间,Winuser.h 中未定义以下索引:
- GWL_WNDPROC
- GWL_HINSTANCE
- GWL_HWNDPARENT
- GWL_USERDATA
相反,Winuser.h 定义了以下新索引:
- GWLP_WNDPROC
- GWLP_HINSTANCE
- GWLP_HWNDPARENT
- GWLP_USERDATA
- GWLP_ID
例如,以下代码不编译:
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);
应按如下所示对其进行更改:
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
设置 WNDCLASS 结构的 cbWndExtra 成员时,请务必为指针保留足够的空间。 例如,如果当前为指针值保留 size (DWORD) 字节,则保留 size 为 (DWORD_PTR) 个字节。
使用 FIELD_OFFSET 访问所有窗口和类数据。
使用硬编码偏移量访问窗口数据很常见。 此方法不可移植到 64 位 Windows。 若要使代码可移植,请使用 FIELD_OFFSET 宏访问窗口和类数据。 不要假定第二个指针的偏移量为 4。
LPARAM、WPARAM 和 LRESULT 类型会随平台更改大小。
编译 64 位代码时,这些类型会扩展到 64 位,因为它们通常包含指针或整型类型。 请勿将这些值与 DWORD、 ULONG、 UINT、 INT、 int 或 long 值混合使用。 检查如何使用这些类型,并确保不会无意中截断值。