Распространенные ошибки компилятора
В этом разделе показаны типичные ошибки компилятора, возникающие при переносе существующей базы кода. Эти примеры относятся к коду HAL на уровне системы, хотя основные понятия напрямую применимы к коду на уровне пользователя.
Пример 1 предупреждения C4311
"тип приведения": усечение указателя с "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-разрядном мире. Решение заключается в получении 64-разрядного указателя на ULONG, так как изменение определения объединения, которое pPciAddr-u.AsULONG> определяется в изменениях слишком много кода.
Использование макроса PtrToUlong для преобразования 64-разрядного PVOID в необходимый ULONG разрешено, так как у нас есть знания о конкретном значении CIA_PCI_CONFIG_BASE_QVA. В этом случае этот указатель никогда не будет содержать данные в верхних 32 битах.
-
Решение
-
pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);
Пример 2 предупреждения C4311
"тип приведения": усечение указателя от "структуры _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 );
Пример 1 предупреждения C4244
'=' : преобразование из структуры _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-разрядный указатель в 32-разрядных и 64-разрядных моделях, так как он объявлен POINTER_32. Оригинальный конструктор этой структуры знал, что он будет использоваться в 32-разрядном контексте в BIOS и четко определил его для этого использования. Этот код работает хорошо в 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 Example 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
"тип приведения": усечение указателя от "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-разрядным для 32-разрядной Ос Windows и 64-разрядных версий для 64-разрядной Ос Windows. Он указывает на целое число без знака, ULONG_PTR, то есть 32 бита для 32-разрядной Windows и 64-разрядных версий для 64-разрядной Windows.
Предупреждение C4311 — пример 2
"тип приведения": усечение указателя от "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 (Quasi Virtual Address) действительно являются 32-разрядными значениями на этом этапе и будут соответствовать ULONG, они более последовательны для обработки всех адресов как ULONG_PTR значений, когда это возможно.
Указатель PciIoSpaceBase содержит QVA, созданный в макросе HAL_MAKE_QVA. Этот макрос возвращает 64-разрядное значение с 32-разрядными 32-разрядными 32-разрядными значениями, поэтому математические вычисления будут работать. Мы могли бы просто оставить код, чтобы усечь указатель на ULONG, но эта практика не рекомендуется повысить удобство обслуживания и переносимость кода. Например, содержимое QVA может измениться в будущем, чтобы использовать некоторые верхние биты на этом уровне, нарушая код.
-
Решение
-
Будьте безопасными и используйте ULONG_PTR для всех математических вычислений адресов и указателей.
HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
Пример 3 предупреждения C4311
"тип приведения": усечение указателя от "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 ) );
Пример 4 предупреждения C4311
"тип приведения": усечение указателя от "void *__ptr64" до "unsigned long"
-
Код
-
TranslatedAddress->LowPart = (ULONG)HalCreateQva( *TranslatedAddress, va);
-
Описание
-
ПереводedAddress — это объединение, которое выглядит примерно так:
typedef union Struct { ULONG LowPart; LONG Highpart; } LONGLONG QuadPart; }
-
Решение
-
Зная, что остальной код может размещаться в Highpart, мы можем выбрать любой из решений, показанных здесь.
TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );
Макрос PtrToUlong усечение указателя, возвращаемого HalCreateQva , на 32 бита. Мы знаем, что QVA, возвращаемая HalCreateQva , имеет верхний 32 бита, равный нулю, и самая следующая строка кода задает для переводаedAddress-Highpart> значение нулю.
С осторожностью мы можем использовать следующее:
TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);
Это работает в этом примере: макрос HalCreateQva возвращает 64 бита, а верхний 32 бит — ноль. Просто будьте осторожны, чтобы не оставить верхние 32 бита неопределенным в 32-разрядной среде, что на самом деле может сделать это второе решение.
Пример 5 предупреждения C4311
"тип приведения": усечение указателя от "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 становится просто четырехкратным, сдвигом вправо и сохранением долгого времени.