다음을 통해 공유


일반적인 컴파일러 오류

이 섹션에서는 기존 코드 베이스를 마이그레이션할 때 발생하는 일반적인 컴파일러 오류를 보여 줍니다. 이러한 예제는 개념이 사용자 수준 코드에 직접 적용 가능하지만 시스템 수준 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비트 세계에서 오류가 발생합니다. pPciAddr-u.AsULONG>이 너무 많은 코드 변경에 정의되어 있는 공용 구조체의 정의를 변경하기 때문에 ULONG에 대한 64비트 포인터를 가져오는 것이 솔루션입니다.

매크로 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는 포인터이므로 프로그래밍 모델에서 크기를 변경합니다. 마지막 매개 변수가 ULONG_PTR 있도록 KeBugCheckEx의 프로토타입변경되었습니다. 따라서 함수 포인터를 ULONG_PTR 캐스팅해야 합니다.

PVOID가 마지막 매개 변수로 사용되지 않은 이유를 물어볼 수 있습니다. 호출 컨텍스트에 따라 마지막 매개 변수는 포인터 또는 오류 코드가 아닌 것일 수 있습니다.

솔루션

KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );

경고 C4244 예제 1

'=': '구조체 _CONFIGURATION_COMPONENT *__ptr64'에서 '구조체 _CONFIGURATION_COMPONENT *'로 변환, 데이터 손실 가능성

코드

Component = &CurrentEntry->ComponentEntry;

Description

함수는 변수 구성 요소를 PCONFIGURATION_COMPONENT 선언합니다. 나중에 변수가 올바른 것으로 표시되는 다음 할당에서 사용됩니다.

Component = &CurrentEntry->ComponentEntry;

그러나 PCONFIGURATION_COMPONENT 형식은 다음과 같이 정의됩니다.

typedef struct __CONFIGURATION_COMPONENT {
...
...
} CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;

PCONFIGURATION_COMPONENT 대한 형식 정의는 POINTER_32 선언되므로 32비트 및 64비트 모델 모두에서 32비트 포인터를 제공합니다. 이 구조의 원래 디자이너는 BIOS의 32비트 컨텍스트에서 사용될 것이라는 것을 알고 있었고 이를 위해 명시적으로 정의했습니다. 이 코드는 포인터가 32비트이므로 32비트 Windows에서 잘 작동합니다. 64비트 Windows에서는 코드가 64비트 컨텍스트에 있으므로 작동하지 않습니다.

솔루션

이 문제를 해결하려면 32비트 PCONFIGURATION_COMPONENT 대신 CONFIGURATION_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(Quasi Virtual Address) 값은 이 단계에서 실제로 32비트 값이며 ULONG적합하지만 가능한 경우 모든 주소를 ULONG_PTR 값으로 처리하는 것이 더 일관됩니다.

포인터 PciIoSpaceBase는 매크로 HAL_MAKE_QVA 만들어진 QVA를 보유합니다. 이 매크로는 상위 32비트가 0으로 설정된 64비트 값을 반환하므로 수학이 작동합니다. 단순히 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 대신 ULONG_PTR 사용합니다.

솔루션

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비트는 0으로 설정되고 바로 다음 코드 줄은 TranslatedAddress-Highpart>를 0으로 설정합니다.

주의해서 다음을 사용할 수 있습니다.

TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);

이 예제 에서는 HalCreateQva 매크로가 64비트를 반환하며 상위 32비트는 0으로 설정됩니다. 이 두 번째 솔루션이 실제로 수행할 수 있는 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비트에서 유효하다는 것을 알고 있습니다. 이렇게 하면 64비트 포인터가 32비트 ULONG으로 잘리기 때문에 PtrToUlong 매크로를 사용할 수 있습니다. PtrToUlong에는 포인터 인수가 필요하기 때문에 PVOID 캐스트가 필요합니다. 결과 어셈블러 코드를 보면 이 모든 C 코드 캐스팅은 로드 쿼드, 오른쪽으로 이동 및 긴 저장이 됩니다.