常见编译器错误
本部分介绍迁移现有代码库时发生的典型编译器错误。 这些示例恰好来自系统级 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声明为 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 );
-
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 位 ULONG。 PVOID 强制转换是必需的,因为 PtrToUlong 需要指针参数。 查看生成的汇编程序代码时,所有这些 C 代码强制转换都只是一个加载象限,向右移动,并存储较长。