常见编译器错误

本部分说明了迁移现有代码库时发生的典型编译器错误。 这些示例发生在系统级 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”:从“结构_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

“=”:从“结构_CONFIGURATION_COMPONENT*__ptr64”转换为“结构_CONFIGURATION_COMPONENT*”,可能会丢失数据

代码

Component = &CurrentEntry->ComponentEntry;

说明

该函数将变量组件声明为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 的 32 位,对于 64 位 Windows,则为 64 位。 它指向 32 位 Windows 的无符号整数(ULONG_PTR)和 64 位 Windows 的 32 位。

警告 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 保存宏HAL_MAKE_QVA中创建的 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 代码强制转换都只是一个负载象限、右移和存储长。