다음을 통해 공유


포인터 사용에 대한 규칙

32비트 및 64비트 Microsoft Windows 모두에 대해 컴파일하도록 코드를 포팅하는 것은 간단합니다. 포인터 캐스팅에 대한 몇 가지 간단한 규칙만 따르고 코드에서 새 데이터 형식을 사용해야 합니다. 포인터 조작 규칙은 다음과 같습니다.

  1. int, long, ULONG 또는 DWORD포인터를 캐스팅하지 마세요.

    포인터를 캐스팅하여 일부 비트를 테스트하거나, 비트를 설정하거나 지우거나, 그렇지 않으면 해당 내용을 조작해야 하는 경우 UINT_PTR 또는 INT_PTR 형식을 사용합니다. 이러한 형식은 32비트 및 64비트 Windows 모두에 대한 포인터 크기로 크기가 조정되는 정수 계열 형식입니다(예: 32비트 Windows의 경우 ULONG , 64비트 Windows의 경우 _int64). 예를 들어 다음 코드를 포팅하고 있다고 가정합니다.

    ImageBase = (PVOID)((ULONG)ImageBase | 1);

    포팅 프로세스의 일부로 다음과 같이 코드를 변경합니다.

    ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);

    적절한 경우 UINT_PTRINT_PTR 사용합니다(그리고 필요한지 확실하지 않은 경우 만일의 경우를 대비하여 사용하는 데 아무런 해가 없습니다). 포인터를 ULONG, LONG, INT, UINT 또는 DWORD 형식으로 캐스팅하지 마세요.

    HANDLE은 void*정의되므로 64비트 Windows에서 낮은 순서의 2비트를 테스트, 설정 또는 지우기 위해 HANDLE 값을 ULONG 값으로 형식 캐스팅하는 것은 오류입니다.

  2. PtrToLong 또는 PtrToUlong 함수를 사용하여 포인터를 자립니다.

    32비트 값에 대한 포인터를 잘라내야 하는 경우 PtrToLong 또는 PtrToUlong 함수(Basetsd.h에 정의됨)를 사용합니다. 이러한 함수는 호출 기간 동안 포인터 잘림 경고를 사용하지 않도록 설정합니다.

    이러한 함수를 신중하게 사용합니다. 이러한 함수 중 하나를 사용하여 포인터 변수를 변환한 후에는 포인터로 다시 사용하지 마세요. 이러한 함수는 주소의 상위 32비트를 자른다. 이 비트는 일반적으로 포인터로 원래 참조된 메모리에 액세스하는 데 필요합니다. 신중하게 고려하지 않고 이러한 함수를 사용하면 코드가 취약합니다.

  3. 64비트 코드로 컴파일될 수 있는 코드에서 POINTER_32 값을 사용할 때는 주의해야 합니다. 컴파일러는 포인터를 0 확장하지 않고 64비트 코드의 네이티브 포인터에 할당될 때 포인터를 서명 확장합니다.

  4. 32비트 코드로 컴파일될 수 있는 코드에서 POINTER_64 값을 사용할 때는 주의해야 합니다. 컴파일러는 포인터를 0 확장하지 않고 32비트 코드로 포인터를 서명 확장합니다.

  5. OUT 매개 변수를 사용하여 주의해야 합니다.

    예를 들어 다음과 같이 정의된 함수가 있다고 가정합니다.

    void func( OUT PULONG *PointerToUlong );

    이 함수는 다음과 같이 호출하지 마세요.

    ULONG ul;
    PULONG lp;
    func((PULONG *)&ul);
    lp = (PULONG)ul;
    

    대신 다음 호출을 사용합니다.

    PULONG lp;
    func(&lp);
    

    Typecasting &ul to PULONG* 은 컴파일러 오류를 방지하지만 함수는 64비트 포인터 값을 ul의 메모리에 씁니다. 이 코드는 32비트 Windows에서 작동하지만 64비트 Windows에서 데이터 손상이 발생하고 미묘하고 찾기 어려운 손상이 발생합니다. 결론 : C 코드로 트릭을 재생하지 마십시오 - 간단하고 간단합니다.

  6. 다형 인터페이스에 주의하세요.

    다형 데이터에 대한 DWORD 매개 변수를 허용하는 함수를 만들지 마세요. 데이터가 포인터 또는 정수 값일 수 있는 경우 UINT_PTR 또는 PVOID 형식을 사용합니다.

    예를 들어 DWORD 값으로 형식화된 예외 매개 변수 배열을 허용하는 함수를 만들지 마세요. 배열은 DWORD_PTR 값의 배열이어야 합니다. 따라서 배열 요소는 주소 또는 32비트 정수 값을 보유할 수 있습니다. (일반적인 규칙은 원래 형식 이 인 경우DWORD이고 포인터 너비여야 하며 DWORD_PTR 값으로 변환해야 합니다. 따라서 해당 포인터 정밀도 형식이 있습니다.) 다형적 방식으로 DWORD, ULONG 또는 기타 32비트 형식을 사용하는 코드가 있는 경우(즉, 매개 변수 또는 구조체 멤버가 주소를 저장하려는 경우) 현재 형식 대신 UINT_PTR 사용합니다.

  7. 새 창 클래스 함수를 사용합니다.

    포인터가 포함된 창 또는 클래스 프라이빗 데이터가 있는 경우 코드는 다음 새 함수를 사용해야 합니다.

    이러한 함수는 32비트 및 64비트 Windows 모두에서 사용할 수 있지만 64비트 Windows에서 필요합니다. 지금 이러한 함수를 사용하여 전환을 준비합니다.

    또한 64비트 Windows의 새 함수를 사용하여 클래스 프라이빗 데이터의 포인터 또는 핸들에 액세스해야 합니다. 이러한 경우를 찾는 데 도움이 되도록 64비트 컴파일 중에 Winuser.h에서 다음 인덱스가 정의되지 않습니다.

    • GWL_WNDPROC
    • GWL_HINSTANCE
    • GWL_HWNDPARENT
    • GWL_USERDATA

    대신 Winuser.h는 다음과 같은 새 인덱스를 정의합니다.

    • GWLP_WNDPROC
    • GWLP_HINSTANCE
    • GWLP_HWNDPARENT
    • GWLP_USERDATA
    • GWLP_ID

    예를 들어 다음 코드는 컴파일되지 않습니다.

    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);

    다음과 같이 변경해야 합니다.

    SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);

    WNDCLASS 구조체의 cbWndExtra 멤버를 설정할 때 포인터에 충분한 공간을 예약해야 합니다. 예를 들어 포인터 값에 대한 sizeof(DWORD) 바이트를 현재 예약하는 경우 sizeof(DWORD_PTR) 바이트를 예약합니다.

  8. FIELD_OFFSET 사용하여 모든 창 및 클래스 데이터에 액세스합니다.

    하드 코딩된 오프셋을 사용하여 창 데이터에 액세스하는 것이 일반적입니다. 이 기술은 64비트 Windows로 이식할 수 없습니다. 코드를 이식 가능하도록 하려면 FIELD_OFFSET 매크로를 사용하여 창 및 클래스 데이터에 액세스합니다. 두 번째 포인터의 오프셋이 4라고 가정하지 마세요.

  9. LPARAM, WPARAMLRESULT 형식은 플랫폼에 따라 크기를 변경합니다.

    64비트 코드를 컴파일할 때 이러한 형식은 일반적으로 포인터 또는 정수 형식을 보유하기 때문에 64비트로 확장됩니다. 이러한 값을 DWORD, ULONG, UINT, INT, int 또는 long 값과 혼합하지 마세요. 이러한 형식을 사용하는 방법을 검사하고 실수로 값을 자르지 않는지 확인합니다.