共用方式為


使用指標的規則

移植程式代碼以針對 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 (如果您不確定是否需要它們,則只是以防萬一使用它們沒有任何傷害)。 請勿將您的指標轉換成ULONG、LONGINTUINTDWORD 類型

    請注意,HANDLE 定義為 void*,因此將 HANDLE 值鍵入 ULONG 值以測試、設定或清除低序 2 位是 64 位 Windows 上的錯誤。

  2. 使用 PtrToLongPtrToUlong 函式來截斷指標。

    如果您必須截斷 32 位值的指標,請使用 PtrToLongPtrToUlong 函式(定義於 Basetsd.h 中)。 這些函式會在呼叫期間停用指標截斷警告。

    請仔細使用這些函式。 使用下列其中一個函式轉換指標變數之後,請勿再將它當做指標使用。 這些函式會截斷位址的上層 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);
    

    Typecasting &ul to 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 成員時,請務必為指標保留足夠的空間。 例如,如果您目前保留指標值的 sizeof(DWORD) 位元組,請保留 sizeof(DWORD_PTR) 個字節。

  8. 使用 FIELD_OFFSET 存取所有視窗和類別數據。

    使用硬式編碼位移來存取視窗數據很常見。 這項技術無法移植到64位 Windows。 若要讓您的程式代碼可攜式,請使用 FIELD_OFFSET 宏存取視窗和類別數據。 請勿假設第二個指標的位移為 4。

  9. LPARAMWPARAMLRESULT 類型會隨著平台變更大小。

    編譯 64 位程式代碼時,這些類型會擴充為 64 位,因為它們通常會保存指標或整數型別。 請勿將這些值與 DWORDULONGUINTINT、intlong 值混合。 檢查如何使用這些類型,並確定您不會不小心截斷值。