常见编译器错误

本部分介绍迁移现有代码库时发生的典型编译器错误。 这些示例恰好来自系统级 HAL 代码,尽管这些概念直接适用于用户级代码。

警告 C4311 示例 1

“type cast”:从“void *__ptr64”到“unsigned long”的指针截断

代码

pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;

Description

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 );

Description

问题是,此函数的最后一个参数是指向数据结构的指针。 由于 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;

Description

函数将变量 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;

Description

生成此警告是因为计算使用 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;
}

Description

此整个函数将地址作为整数处理,因此需要以可移植的方式键入这些整数。 所有局部变量、计算中的中间值和返回值都应是可移植类型。

解决方案

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 位。 它指向一个无符号整数 ULONG_PTR,即 32 位 Windows 的 32 位和 64 位 Windows 的 64 位。

警告 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);

Description

尽管所有 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);
}

Description

如果 () & 和左移 << () 运算符应用于指针类型,编译器会警告这些运算符的地址。 在上面的代码中,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);

Description

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;

Description

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 代码强制转换都只是一个加载象限,向右移动,并存储较长。