共用方式為


常見的編譯程序錯誤

本節說明移轉現有程序代碼基底時發生的一般編譯程序錯誤。 這些範例恰好來自系統層級 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宣告為 PVOIDULONG 轉型在 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 位 ULONGPVOID 轉換是必要的,因為 PtrToUlong 需要指標自變數。 當您查看產生的組合器程式代碼時,所有這些 C 程式代碼轉換只會變成負載四邊形、向右移和儲存long。