指针使用规则

移植代码以同时编译 32 位和 64 位 Microsoft Windows 非常简单。 只需遵循几个有关转换指针的简单规则,并在代码中使用新的数据类型。 指针操作的规则如下所示。

  1. 不要将指针强制转换为 intlongULONGDWORD

    如果必须强制转换指针以测试某些位、设置或清除位,或者以其他方式操作其内容,请使用 UINT_PTRINT_PTR 类型。 这些类型是可缩放到 32 位和 64 位 Windows (指针大小的整型类型,例如,32 位 Windows 的 ULONG 和 64 位 Windows) 的 _int64。 例如,假设要移植以下代码:

    ImageBase = (PVOID)((ULONG)ImageBase | 1);

    作为移植过程的一部分,你将更改代码,如下所示:

    ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);

    在适当 情况下使用UINT_PTRINT_PTR (如果不确定是否需要它们,则使用它们不会造成损害,以防) 。 不要将指针强制转换为 ULONGLONGINTUINTDWORD 类型。

    请注意, HANDLE 定义为 void*,因此将 HANDLE 值类型化为 ULONG 值以测试、设置或清除低阶 2 位在 64 位 Windows 上是一个错误。

  2. 使用 PtrToLongPtrToUlong 函数截断指针。

    如果必须截断指向 32 位值的指针,请使用 Basetsd.h) 中定义的 PtrToLongPtrToUlong 函数 (。 这些函数在调用期间禁用指针截断警告。

    请谨慎使用这些函数。 使用这些函数之一转换指针变量后,再也不要将其用作指针。 这些函数截断地址的上限 32 位,通常需要这些地址来访问最初由指针引用的内存。 如果不仔细考虑这些函数,将导致代码脆弱。

  3. 在可能编译为 64 位代码的代码中使用POINTER_32值时要小心。 当指针在 64 位代码中分配给本机指针时,编译器将对指针进行签名扩展,而不是对指针进行零扩展。

  4. 在可能编译为 32 位代码的代码中使用POINTER_64值时要小心。 编译器将使用 32 位代码对指针进行签名扩展,而不是对指针进行零扩展。

  5. 使用 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 代码玩技巧 - 简单直接更好。

  6. 使用多态接口时要小心。

    不要创建接受多态数据的 DWORD 参数的函数。 如果数据可以是指针或整型值,请使用 UINT_PTR 或 PVOID 类型。

    例如,不要创建接受类型为 DWORD 值的异常参数数组的函数。 数组应该是 DWORD_PTR 值的数组。 因此,数组元素可以保存地址或 32 位整型值。 (一般规则是,如果原始类型为 DWORD 并且需要指针宽度,请将其转换为 DWORD_PTR 值。这就是存在相应的指针精度类型的原因。) 如果代码以多态方式使用 DWORDULONG 或其他 32 位类型 (,即,你确实希望参数或结构成员持有地址) ,请使用 UINT_PTR 代替当前类型。

  7. 使用新的窗口类函数。

    如果具有包含指针的窗口或类私有数据,则代码将需要使用以下新函数:

    这些函数可以在 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) 个字节。

  8. 使用 FIELD_OFFSET 访问所有窗口和类数据。

    使用硬编码偏移量访问窗口数据很常见。 此方法不可移植到 64 位 Windows。 若要使代码可移植,请使用 FIELD_OFFSET 宏访问窗口和类数据。 不要假定第二个指针的偏移量为 4。

  9. LPARAMWPARAMLRESULT 类型会随平台更改大小。

    编译 64 位代码时,这些类型会扩展到 64 位,因为它们通常包含指针或整型类型。 请勿将这些值与 DWORDULONGUINTINTintlong 值混合使用。 检查如何使用这些类型,并确保不会无意中截断值。