얼마 전까지만 해도 대부분의 시스템은 32비트 기반이었으나, 이제는 64비트 시스템이 주류를 이루기 시작했다.


64비트와 32비트

CPU는 I/O 버스를 통해서 데이터를 외부로 전송하기도 하고, CPU 내부로 수신하기도 한다. 
([IT/Windows Programmming] - CPU에 대한 이해 참조)
이때 한 번에 전송 및 수신할 수 있는 데이터의 크기에 따라서 32비트 시스템과 64비트 시스템이 나뉘게 된다. 또한, 32비트 컴퓨터는 한 번에 32비트 데이터를, 64비트 컴퓨터는 한 번에 64비트 데이터를 처리할 수 있다.

"한 번에 송수신할 수 있는 데이터 크기와 한 번에 처리할 수 있는 데이터 크기를 기준으로 32비트와 64비트 컴퓨터를 구분 짓는다" 라고 할 수 있겠다.



프로그래머 입장에서의 64비트 컴퓨터

32비트 컴퓨터에서는 주소값 표현을 위해 32비트를 사용한다. 32비트로 주소값을 표현할 수 있는 범위는 4G바이트(2의 32승) 이므로 RAM을 4G 까지 활용할 수 있다.
64비트 컴퓨터에서는 주소값 표현을 위해 64비트를 사용하므로 18,446,744테라바이트(2의 64승) 까지 RAM을 활용할 수 있다.
이전까지는 4G 이상의 RAM을 활용할 일이 적었으나, 최근에는 데스크탑 컴퓨터에서도 4G 이상의 RAM을 장착하여 사용하는 일이 흔해지면서 64비트 컴퓨터에 대한 수요가 많이 증가하고 있는 추세다.



LLP 64 vs LP64

32비트 환경에서는 int, long, 그리고 포인터 모두 4바이트로 표현된다. 그러나 64비트 컴퓨터에서는 다음과 같이 표현된다.

 운영체제 모델 char short  int long 포인터
 Windows LLP64 1바이트 2바이트 4바이트 4바이트 8바이트
 UNIX LP64 1바이트 2바이트 4바이트 8바이트 8바이트

Windows 에서는 LLP64라는 데이터 표현 모델을 따르는데, 이 모델은 int와 long은 그대로 4바이트로 표현하고, 포인터만 8바이트로 표현하는 방식이다. 따라서 32비트 시스템과의 호환성을 중시한 모델이라고 할 수 있다.



64비트와 32비트 공존의 문제점

#include <stdio.h>
int main()
{
    int arr[10] = {0, };
    int arrVal = (int)arr;
    printf("pointer : %d \n", arrVal);
    return 0;
}

위 예제에서는 배열이 선언된 주소값을 출력하기 위해 int형 변수에 그 값을 저장하고 있다. int형 자료형도, 포인터도 4바이트로 표현되는 32비트 컴퓨터에서는 아무런 문제가 되지 않았다. 그러나 64비트 시스템에서는 문제가 될 수 있다.
64비트 Windows 시스템에서는 LLP64가 기본 모델이므로 int는 4바이트, 주소값인 포인터는 8바이트로 표현되므로 위 예제는 형 변환 과정에서 데이터 손실이 발생할 수 있다. 따라서 배열arr이 4G 이하의 메모리 영역에 할당되어 4바이트로 주소값 표현이 가능하다면 데이터 손실이 발생하지 않겠지만, 그 이상의 메모리 영역이 선언된다면 데이터 손실이 발생해서 배열의 정확한 주소값을 확인하지 못하게 된다.



Windows 스타일 자료형


기본 자료형에 대한 마이크로소프트의 정의
 WINDOWS 자료형 의미 정의 형태
 BOOL Boolean variable typedef bool BOOL
 DWORD 32-bit unsigned integer typedef unsigned long DWORD;
 INT 32-bit signed integer typedef int INT
 LONG 32-bit signed integer typedef long LONG
 UINT Unsigned INT typedef unsigned int UINT
 ULONG Unsigned LONG typedef unsigned long ULONG


포인터에 대한 마이크로소프트의 정의
 WINDOWS 자료형 의미 정의 형태
 PINT INT에 대한 포인터 typedef int* PINT
 PLONG LONG에 대한 포인터 typedef LONG* PLONG
 PUINT UINT에 대한 포인터 typedef unsigned int* PUINT
 PULONG ULONG에 대한 포인터 typedef ULONG* PULONG



Polymorphic 자료형

마이크로소프트에서는 WIN64 기반으로 넘어가면서 Polymorphic 자료형을 정의하고 있다. 이 Polymorphic이라는 단어는 "다양한 모습이 있는, 다형적" 이라는 뜻으로 해석된다. 마이크로소프트에서 정의하고 있는 Polymorphic 자료형의 정의 형태는 다음과 같다.

마이크로소프트에서 정의하고 있는 Polymorphic 자료형의 정의 형태
#if defined(_WIN64)
    typedef __int64 LONG_PTR;
    typedef unsigned __int64 ULONG_PTR;
    typedef __int 64 INT_PTR;
    typedef unsigned __int64 UINT_PTR;
#else
    typedef long LONG_PTR;
    typedef unsigned long ULONG_PTR;
    typedef int INT_PTR;
    typedef unsigned int UINT_PTR;
#endif

이름에 PTR이 붙어서 자료형 자체가 포인터라고 생각하기 쉬운데, 그렇지 않다. 포인터값 기반의 산술연산을 위해 정의된 자료형이기에 PTR이라는 이름을 붙인 것이다. 위에서 설명했드시 32비트 시스템과 64비트 시스템의 포인터 크기가 다르기 때문에 이 문제를 해결하기 위해 등장한 자료형이다.

두 변수의 메모리 공간상 거리를 계산해 주는 함수를 Polymorphic 자료형으로 만들어 보겠다.

예제 - 두 변수의 메모리 공간상 거리를 계산해 주는 함수
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

UINT_PTR CalDistance(UINT_PTR a, UINT_PTR b)
{
    return a-b;
}

int _tmain()
{
    INT32 val1 = 10;
    INT32 val2 = 20;

    _tprintf
    (
        _T("distance : %d \n"),
        CalDistance((UINT_PTR)&val1, (UINT_PTR)&val2)
    );

    return 0;
}

예제에서 선언된 UINT_PTR은 Polymorphic자료형이다. 따라서 64비트 환경에서는 64비트로, 32비트 환경에서는 32비트로 선언된다.
실제로 개발과정에서 이러한 Polymorphic 자료형 변수를 우리가 직접 선언하는 일은 드물지만, Windows에서 제공하는 시스템 함수 중 일부는 이 자료형을 기반으로 구현되어 있으므로, 이러한 자료형이 선언된 것을 해석하는데 도움이 될 것이다.

위 코드에서의 _tmain, _tprintf, _T 등이 낯설게 느껴진다면
[IT/Windows Programmming] - MBCS와 WBCS의 동시 지원
글을 참조하면 이해에 큰 도움이 될 것이다.