Regeln für die Verwendung von Zeigern
Das Portieren Ihres Codes zum Kompilieren 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.
Wandeln Sie Zeiger nicht in int, long, ULONG oder DWORD um.
Wenn Sie einen Zeiger umwandeln müssen, um einige Bits zu testen, Bits festzulegen oder zu löschen oder den Inhalt anderweitig zu bearbeiten, verwenden Sie den UINT_PTR - oder INT_PTR-Typ . 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 ändern Sie den Code wie folgt:
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);
Verwenden Sie ggf. UINT_PTR und INT_PTR (und wenn Sie unsicher sind, ob sie erforderlich sind, schadet die Verwendung nicht nur für den Fall). Wandeln Sie Ihre Zeiger nicht auf die Typen ULONG, LONG, INT, UINT oder DWORD um.
Beachten Sie, dass HANDLE als void* definiert ist. Daher ist das Typecasting eines HANDLE-Werts auf einen ULONG-Wert zum Testen, Festlegen oder Löschen der 2 Bits mit niedriger Reihenfolge unter 64-Bit-Windows ein Fehler.
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 PtrToLong - oder PtrToUlong-Funktion (definiert in Basetsd.h). Diese Funktionen deaktivieren die Zeigerabschneidungswarnung 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 erforderlich sind, um auf den Speicher zuzugreifen, auf den ursprünglich der Zeiger verweist. Die Verwendung dieser Funktionen ohne sorgfältige Berücksichtigung führt zu fragilem Code.
Seien Sie vorsichtig, wenn Sie POINTER_32 Werte in Code verwenden, der möglicherweise als 64-Bit-Code kompiliert werden kann. Der Compiler signiert den Zeiger, wenn er einem nativen Zeiger im 64-Bit-Code zugewiesen ist, und nicht den Zeiger mit null erweitern.
Seien Sie vorsichtig, wenn Sie POINTER_64 Werte in Code verwenden, der möglicherweise als 32-Bit-Code kompiliert werden kann. Der Compiler signiert den Zeiger im 32-Bit-Code und nicht im Null-Erweitern des Zeigers.
Seien Sie vorsichtig bei der Verwendung von OUT-Parametern.
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 in den Speicher unter &ul. Dieser Code funktioniert unter 32-Bit-Windows, führt aber zu Datenbeschädigungen unter 64-Bit-Windows und ist subtile, schwer zu findende Beschädigung. Fazit: Spielen Sie keine Tricks mit dem C-Code – einfach und einfach ist besser.
Seien Sie vorsichtig mit polymorphen Schnittstellen.
Erstellen Sie keine Funktionen, die DWORD-Parameter für polymorphe Daten akzeptieren. Wenn es sich bei den Daten um einen Zeiger oder einen integralen Wert handeln kann, verwenden Sie den Typ UINT_PTR oder PVOID .
Erstellen Sie beispielsweise keine Funktion, die ein Array von Ausnahmeparametern akzeptiert, die als DWORD-Werte eingegeben werden. Das Array sollte ein Array von DWORD_PTR Werten sein. Daher können die Arrayelemente Adressen oder 32-Bit-Integralwerte enthalten. (Die allgemeine Regel ist, dass, wenn der ursprüngliche Typ DWORD ist und die Zeigerbreite sein muss, ihn in einen DWORD_PTR Wert konvertieren. Deshalb gibt es entsprechende Zeigergenauigkeitstypen.) Wenn Sie Über Code verfügen, der DWORD, ULONG oder andere 32-Bit-Typen auf polymorphe Weise verwendet (d. h. der Parameter oder Strukturmember soll wirklich eine Adresse enthalten), verwenden Sie UINT_PTR anstelle des aktuellen Typs.
Verwenden Sie die neuen Fensterklassenfunktionen.
Wenn Sie über private Fenster- oder Klassendaten verfügen, die Zeiger enthalten, muss Ihr Code die folgenden neuen Funktionen verwenden:
Diese Funktionen können sowohl unter 32- als auch unter 64-Bit-Windows verwendet werden, sind aber unter 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 unter 64-Bit-Windows auf Zeiger oder Handles in privaten Daten der Klasse zugreifen. Um Ihnen bei der Suche nach diesen Fällen zu helfen, werden die folgenden Indizes während einer 64-Bit-Kompilierung nicht in Winuser.h definiert:
- GWL_WNDPROC
- GWL_HINSTANCE
- GWL_HWNDPARENT
- GWL_USERDATA
Stattdessen definiert Winuser.h die folgenden neuen Indizes:
- GWLP_WNDPROC
- GWLP_HINSTANCE
- GWLP_HWNDPARENT
- GWLP_USERDATA
- GWLP_ID
Der folgende Code wird beispielsweise nicht kompiliert:
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);
Es 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 z. B. derzeit sizeof(DWORD)-Bytes für einen Zeigerwert reservieren, reserve sizeof(DWORD_PTR) bytes.
Greifen Sie mit FIELD_OFFSET auf alle Fenster- und Klassendaten zu.
Es ist üblich, mit hartcodierten Offsets auf Fensterdaten zuzugreifen. Dieses Verfahren ist nicht auf 64-Bit-Windows portierbar. Um Ihren Code portierbar zu machen, greifen Sie mithilfe des makros FIELD_OFFSET auf Ihre Fenster- und Klassendaten zu. Gehen Sie nicht davon aus, dass der zweite Zeiger einen Offset von 4 aufweist.
Die Typen LPARAM, WPARAM und LRESULT ändern die Größe mit der Plattform.
Beim Kompilieren von 64-Bit-Code werden diese Typen auf 64 Bit erweitert, da sie in der Regel Zeiger oder integrale Typen enthalten. Mischen 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.