常見的編譯程序錯誤
本節說明移轉現有程序代碼基底時發生的一般編譯程序錯誤。 這些範例恰好來自系統層級 HAL 程式代碼,雖然概念直接適用於用戶層級程式代碼。
警告 C4311 範例 1
'type cast':從 'void *__ptr64' 到 'unsigned long 的指標截斷
-
代碼
-
pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;
-
描述
-
PtrToUlong 是內嵌函式或宏,視您的使用量而定。 它會截斷 ULONG 的指標。 雖然 32 位指標不會受到影響,但 64 位指標的上半部會被截斷。
CIA_PCI_CONFIG_BASE_QVA宣告為 PVOID。 ULONG 轉型在 32 位世界中運作,但在 64 位世界中會產生錯誤。 解決方案是取得ULONG的64位指標,因為變更 pPciAddr-u.AsULONG> 在變更太多程式代碼中定義的等位定義。
允許使用宏 PtrToUlong 將 64 位 PVOID 轉換成所需的 ULONG ,因為我們已瞭解CIA_PCI_CONFIG_BASE_QVA的特定值。 在此情況下,此指標永遠不會有上方 32 位的數據。
-
解決方案
-
pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);
警告 C4311 範例 2
'type cast':從 'struct _ERROR_FRAME *__ptr64' 到 'unsigned long' 的指標截斷
-
代碼
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );
-
描述
-
問題是此函式的最後一個參數是數據結構的指標。 因為 PUncorrectableError 是指針,所以它會隨著程式設計模型變更大小。 KeBugCheckEx 的原型已變更,因此最後一個參數是ULONG_PTR。 因此,必須將函式指標 轉換成ULONG_PTR。
您可能會詢問為什麼 PVOID 未當做最後一個參數使用。 根據呼叫的內容,最後一個參數可能不是指標或錯誤碼。
-
解決方案
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );
警告 C4244 範例 1
'=' :從 'struct _CONFIGURATION_COMPONENT *__ptr64' 轉換成 'struct _CONFIGURATION_COMPONENT *',可能遺失數據
-
代碼
-
Component = &CurrentEntry->ComponentEntry;
-
描述
-
函式會將變數 Component 宣告為PCONFIGURATION_COMPONENT。 稍後,變數會用於下列看起來正確的指派中:
Component = &CurrentEntry->ComponentEntry;
不過,類型PCONFIGURATION_COMPONENT定義為:
typedef struct __CONFIGURATION_COMPONENT { ... ... } CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;
PCONFIGURATION_COMPONENT的類型定義會在32位和64位模型中提供32位指標,因為它宣告 為POINTER_32。 此結構的原始設計工具知道它將會用於 BIOS 中的 32 位內容,並明確定義該用途。 此程式代碼在32位 Windows 中正常運作,因為指標恰好是32位。 在 64 位 Windows 中,因為程式代碼位於 64 位內容中,所以無法運作。
-
解決方案
-
若要解決此問題,請使用 CONFIGURATION_COMPONENT * 而不是 32 位PCONFIGURATION_COMPONENT 。 請務必清楚瞭解程序代碼的用途。 如果此程式代碼是要觸碰 32 位 BIOS 或系統空間,此修正將無法運作。
POINTER_32定義於 Ntdef.h 和 Winnt.h 中。
#ifdef (__AXP64__) #define POINTER_32 _ptr32 #else #define POINTER_32 #endif
警告 C4242 範例 2
'=' :從 '__int64' 轉換成 'unsigned long ',可能遺失數據
-
代碼
-
ARC_STATUS HalpCopyNVRamBuffer ( IN PCHAR NvDestPtr, IN PCHAR NvSrcPtr, IN ULONG Length ) { ULONG PageSelect1, ByteSelect1; ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK; ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
-
描述
-
因為計算使用64位值,所以會產生這個警告,在此案例中為指標,並將結果放在32位 ULONG中。
-
解決方案
-
類型將計算 結果轉換成 ULONG ,如下所示:
ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
類型傳送結果可讓編譯程式知道您確定的結果。 話說來,請確定您了解計算,並確實確定它會符合 32 位 ULONG。
如果結果可能不符合 32 位 ULONG,請變更將保存結果的變數基底類型。
警告 C4311 - 範例 1
'type cast' :從 'void *__ptr64' 到 'unsigned long' 的指標截斷
-
代碼
-
ULONG HalpMapDebugPort( IN ULONG ComPort, OUT PULONG ReadQva, OUT PULONG WriteQva) { ULONG ComPortAddress; ULONG PortQva; // Compute the port address, based on the desired com port. switch( ComPort ){ case 1: ComPortAddress = COM1_ISA_PORT_ADDRESS; break; case 2: default: ComPortAddress = COM2_ISA_PORT_ADDRESS; } PortQva = (ULONG)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress; // Return the QVAs for read and write access. *ReadQva = PortQva; *WriteQva = PortQva; return ComPortAddress; }
-
描述
-
此整個函式會將位址當作整數處理,因此需要以可攜式方式輸入這些整數。 計算中的所有局部變數、中繼值,以及傳回值都應該是可攜式類型。
-
解決方案
-
ULONG_PTR HalpMapDebugPort( IN ULONG ComPort, OUT PULONG_PTR ReadQva, OUT PULONG_PTR WriteQva) { ULONG_PTR ComPortAddress; ULONG_PTR PortQva; // Compute the port address, based on the desired com port. switch( ComPort ){ case 1: ComPortAddress = COM1_ISA_PORT_ADDRESS; break; case 2: default: ComPortAddress = COM2_ISA_PORT_ADDRESS; } PortQva = (ULONG_PTR)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress; // Return the QVAs for read and write access. *ReadQva = PortQva; *WriteQva = PortQva; return ComPortAddress; }
PULONG_PTR是 32 位 Windows 的指標,而 64 位 Windows 則是 64 位的指標。 它會指向不帶正負號的整數 ,ULONG_PTR,也就是 32 位 Windows 的 32 位,而 64 位則為 64 位 Windows。
警告 C4311 - 範例 2
'type cast' :從 'void *__ptr64' 到 'unsigned long' 的指標截斷
-
代碼
-
BOOLEAN HalpMapIoSpace ( VOID ) { PVOID PciIoSpaceBase; PciIoSpaceBase = HAL_MAKE_QVA( CIA_PCI_SPARSE_IO_PHYSICAL ); //Map base addresses in QVA space. HalpCMOSRamBase = (PVOID)((ULONG)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
-
描述
-
雖然所有 QVA (准虛擬位址) 值在這個階段都是 32 位值,而且會符合 ULONG,但盡可能將所有地址 視為ULONG_PTR 值會更加一致。
PciIoSpaceBase 指標會保存宏中建立的QVA HAL_MAKE_QVA。 此宏會傳回 64 位值,並將前 32 位設定為零,讓數學能夠運作。 我們可以讓程式代碼將指標截斷為 ULONG,但不建議這種做法增強程式代碼的可維護性和可移植性。 例如,QVA 的內容未來可能會變更為在此層級使用某些上層位,而中斷程序代碼。
-
解決方案
-
安全,並針對所有位址和指標數學使用 ULONG_PTR 。
HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
警告 C4311 範例 3
'type cast' :從 'void *__ptr64' 到 'unsigned long' 的指標截斷
-
代碼
-
PVOID HalDereferenceQva( PVOID Qva, INTERFACE_TYPE InterfaceType, ULONG BusNumber) if ( ((ULONG) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG)Qva << IO_BIT_SHIFT ) ); } else { return (Qva); }
-
描述
-
如果運算子套用至指標類型,編譯程式會警告有關 (&) 和左移位運算符<<的位址。 在上述程序代碼中,Qva是 PVOID 值。 我們需要將這個轉換成整數類型,才能執行數學運算。 因為程式代碼必須是可攜式的,請使用 ULONG_PTR 而非 ULONG。
-
解決方案
-
if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );
警告 C4311 範例 4
'type cast' :從 'void *__ptr64' 到 'unsigned long' 的指標截斷
-
代碼
-
TranslatedAddress->LowPart = (ULONG)HalCreateQva( *TranslatedAddress, va);
-
描述
-
TranslatedAddress 是類似下列的聯集:
typedef union Struct { ULONG LowPart; LONG Highpart; } LONGLONG QuadPart; }
-
解決方案
-
瞭解 Highpart 中其餘程式代碼可能放置的內容,我們可以選取此處所示的其中一個解決方案。
TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );
PtrToUlong 宏會將 HalCreateQva 傳回的指標截斷為 32 位。 我們知道,HalCreateQva 傳回的 QVA 將上限 32 位設定為零,而下一行程式代碼會將 TranslatedAddress-Highpart> 設定為零。
請小心,我們可以使用下列專案:
TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);
這適用於此範例: HalCreateQva 宏會傳回 64 位,並將前 32 位設定為零。 請小心不要讓上層 32 位在 32 位環境中未定義,而這個第二個解決方案實際上可能會這麼做。
警告 C4311 範例 5
'type cast' :從 'void *__ptr64' 到 'unsigned long' 的指標截斷
-
代碼
-
VOID HalpCiaProgramDmaWindow( PWINDOW_CONTROL_REGISTERS WindowRegisters, PVOID MapRegisterBase ) { CIA_WBASE Wbase; Wbase.all = 0; Wbase.Wen = 1; Wbase.SgEn = 1; Wbase.Wbase = (ULONG)(WindowRegisters->WindowBase) >> 20;
-
描述
-
WindowRegisters-WindowBase> 是指針,現在為 64 位。 程序代碼會以滑鼠右鍵按下此值 20 位。 編譯程式不會讓我們在指標上使用右移 (>>) 運算符;因此,我們需要將它轉換成某種整數。
-
解決方案
-
Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));
轉型至 ULONG_PTR 只是我們需要的。 下一個問題是 Wbase。 Wbase 是 ULONG ,且為 32 位。 在此情況下,我們知道 64 位指標 WindowRegisters-WindowBase> 即使在移位之後,在較低的 32 位中仍有效。 這會使用 可接受的 PtrToUlong 宏,因為它會將 64 位指標截斷為 32 位 ULONG。 PVOID 轉換是必要的,因為 PtrToUlong 需要指標自變數。 當您查看產生的組合器程式代碼時,所有這些 C 程式代碼轉換只會變成負載四邊形、向右移和儲存long。