Regeln für die Verwendung von Zeigern

Das Portieren Ihres Codes für die Kompilierung für 32- und 64-Bit-Microsoft Windows ist einfach. Sie müssen nur einige einfache Regeln zum Umwandeln von Zeigern befolgen und die neuen Datentypen in Ihrem Code verwenden. Die Regeln für die Zeigerbearbeitung sind wie folgt.

  1. Geben Sie keine Zeiger in int, long, ULONG oder DWORD um.

    Wenn Sie einen Zeiger zum Testen einiger Bits, Zum Festlegen oder Löschen von Bits oder zum ändern des Inhalts auf andere Weise verwenden müssen, verwenden Sie den Typ UINT _ PTR oder INT _ PTR. Diese Typen sind integrale Typen, die auf die Größe eines Zeigers für 32- und 64-Bit-Windows skaliert werden (z. B. ULONG für 32-Bit-Windows und _ int64 für 64-Bit-Windows). Angenommen, Sie portieren den folgenden Code:

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

    Im Rahmen des Portierungsprozesses würden Sie den Code wie folgt ändern:

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

    Verwenden Sie ggf. UINT _ PTR und INT _ PTR (und wenn Sie unsicher sind, ob sie erforderlich sind, ist es nicht schädlich, sie nur für den Fall zu verwenden). Geben Sie Ihre Zeiger nicht in die Typen ULONG, LONG, INT, UINT oder DWORD um.

    Beachten Sie, dass HANDLE als void * _definiert ist, sodass das Typcasting eines _HANDLE-Werts in einen ULONG-Wert zum Testen, Festlegen oder Löschen der 2 Bits mit niedriger Reihenfolge ein Fehler bei 64-Bit-Windows ist.

  2. Verwenden Sie die PtrToLong- oder PtrToUlong-Funktion, um Zeiger abzuschneiden.

    Wenn Sie einen Zeiger auf einen 32-Bit-Wert abschneiden müssen, verwenden Sie die Funktion PtrToLong oder PtrToUlong (definiert in Basetsd.h). Diese Funktionen deaktivieren die Warnung zum Abschneiden von Zeigern für die Dauer des Aufrufs.

    Verwenden Sie diese Funktionen sorgfältig. Nachdem Sie eine Zeigervariable mit einer dieser Funktionen konvertiert haben, verwenden Sie sie nie wieder als Zeiger. Diese Funktionen kürzen die oberen 32 Bits einer Adresse ab, die normalerweise für den Zugriff auf den Speicher benötigt werden, auf den ursprünglich vom Zeiger verwiesen wird. Die Verwendung dieser Funktionen ohne sorgfältige Berücksichtigung führt zu anfälligen Code.

  3. Seien Sie vorsichtig, wenn Sie POINTER _ 32-Werte im Code verwenden, die als 64-Bit-Code kompiliert werden können. Der Compiler signiert den Zeiger, wenn er einem nativen Zeiger in 64-Bit-Code zugewiesen ist, und erweitert den Zeiger nicht mit 0 (null).

  4. Seien Sie vorsichtig, wenn Sie POINTER _ 64-Werte im Code verwenden, die als 32-Bit-Code kompiliert werden können. Der Compiler signiert den Zeiger in 32-Bit-Code und nicht den Zeiger mit 0 (null).

  5. Seien Sie vorsichtig, wenn Sie OUT-Parameter verwenden.

    Angenommen, Sie haben eine Funktion wie folgt definiert:

    void func( OUT PULONG *PointerToUlong );

    Rufen Sie diese Funktion nicht wie folgt auf.

    ULONG ul;
    PULONG lp;
    func((PULONG *)&ul);
    lp = (PULONG)ul;
    

    Verwenden Sie stattdessen den folgenden Aufruf.

    PULONG lp;
    func(&lp);
    

    Typecasting &ul to PULONG * verhindert einen Compilerfehler, aber die Funktion schreibt einen 64-Bit-Zeigerwert an &ul in den Arbeitsspeicher. Dieser Code funktioniert auf 32-Bit-Windows, verursacht jedoch Datenbeschädigungen auf 64-Bit-Windows und ist eine geringfügige, schwer zu findende Beschädigung. Unterm Strich: Spielen Sie keine Tricks mit dem C-Code – einfach und einfach ist besser.

  6. Seien Sie bei polymorphen Schnittstellen vorsichtig.

    Erstellen Sie keine Funktionen, die DWORD-Parameter für polymorphe Daten akzeptieren. Wenn die Daten ein Zeiger oder ein ganzzahliger Wert sein können, verwenden Sie den UINT _ PTR- oder PVOID-Typ.

    Erstellen Sie beispielsweise keine Funktion, die ein Array von Ausnahmeparametern akzeptiert, die als DWORD-Werte typisiert sind. Das Array sollte ein Array von DWORD _ PTR-Werten sein. Daher können die Arrayelemente Adressen oder ganzzahlige 32-Bit-Werte enthalten. (Allgemeine Regel: Wenn der ursprüngliche Typ DWORD ist und die Zeigerbreite sein muss, konvertieren Sie ihn in einen DWORD _ PTR-Wert. Aus diesem Grund gibt es entsprechende Zeigergenauigkeitstypen.) Wenn Sie über Code verfügen, der DWORD, ULONG oder andere 32-Bit-Typen auf polymorphe Weise verwendet (d. h., sie möchten, dass der Parameter- oder Strukturmember eine Adresse enthält), verwenden Sie UINT _ PTR anstelle des aktuellen Typs.

  7. Verwenden Sie die neuen Fensterklassenfunktionen.

    Wenn Sie über fenster- oder klassenprivate Daten verfügen, die Zeiger enthalten, muss Ihr Code die folgenden neuen Funktionen verwenden:

    Diese Funktionen können sowohl auf 32- als auch auf 64-Bit-Windows verwendet werden, sind jedoch für 64-Bit-Windows erforderlich. Bereiten Sie sich jetzt mit diesen Funktionen auf den Übergang vor.

    Darüber hinaus müssen Sie mithilfe der neuen Funktionen auf 64-Bit-Windows auf Zeiger oder Handles in privaten Klassendaten zugreifen. Um Sie bei der Suche nach diesen Fällen zu unterstützen, werden die folgenden Indizes während einer 64-Bit-Kompilierung in Winuser.h nicht definiert:

    • GWL _ WNDPROC
    • GWL _ HINSTANCE
    • GWL _ HWDPARENT
    • GWL _ USERDATA

    Stattdessen definiert Winuser.h die folgenden neuen Indizes:

    • GWLP _ WNDPROC
    • GWLP _ HINSTANCE
    • GWLP _ HWNDPARENT
    • GWLP _ USERDATA
    • _GWLP-ID

    Beispielsweise wird der folgende Code nicht kompiliert:

    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);

    Sie sollte wie folgt geändert werden:

    SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);

    Achten Sie beim Festlegen des cbWndExtra-Members der WNDCLASS-Struktur darauf, genügend Platz für Zeiger zu reservieren. Wenn Sie beispielsweise derzeit sizeof(DWORD)-Bytes für einen Zeigerwert reservieren, reservieren Sie sizeof(DWORD _ PTR)-Bytes.

  8. Greifen Sie mit FIELD OFFSET auf alle Fenster- und Klassendaten _ zu.

    Es ist üblich, mit hart codierten Offsets auf Fensterdaten zuzugreifen. Diese Technik ist nicht auf 64-Bit-Windows übertragbar. Um Ihren Code portierbar zu machen, greifen Sie mithilfe des FIELD _ OFFSET-Makros auf Ihre Fenster- und Klassendaten zu. Gehen Sie nicht davon aus, dass der zweite Zeiger über einen Offset von 4 verfügt.

  9. Die Typen LPARAM, WPARAM und LRESULT ändern die Größe mit der Plattform.

    Beim Kompilieren von 64-Bit-Code erweitern sich diese Typen auf 64 Bits, da sie in der Regel Zeiger oder ganzzahlige Typen enthalten. Kombinieren Sie diese Werte nicht mit DWORD-, ULONG-, UINT-, INT-, int- oder long-Werten. Untersuchen Sie, wie Sie diese Typen verwenden, und stellen Sie sicher, dass Sie werte nicht versehentlich abschneiden.