Windows 32/64비트 시스템

2025. 2. 26. 16:35개발/Windows

이 글은 윤성우님의 '뇌를 자극하는 윈도우즈 시스템 프로그래밍'을 참고하여 공부한 내용입니다.


 

CPU가 데이터를 처리하기 위해 Bus Interface를 사용하여 메모리와 데이터를 송수신 합니다. 이때 한번에 송수신할 수 있는 데이터 크기에 따라 32비트, 64비트 시스템으로 나뉘게 됩니다. 그리고 그 데이터를 한번에 처리할 수 있는 크기를 뜻하기도 합니다. 그래서 64비트 시스템은 한번에 64비트를 받아 처리할 수 있는 시스템이라는 것 입니다.

 

메모리 주소

32/64비트 시스템은 메모리 주소와 밀접한 관련이 있습니다. 운영체제는 실제 메모리에 주소를 할당하여 해당 메모리에 접근하여 사용할 수 있게 해줍니다. 이때 메모리의 주소의 범위는 시스템에 따라 32/64비트로 표현됩니다. 그래서 32비트에서는 사용할 수 있는 메모리의 최대 크기는 4GB정도가 됩니다. 즉 물리적인 메모리가 4GB 이상 있어도 4GB만 사용할 수 있는 것 입니다.

 

그렇다면 32비트 시스템에서 메모리의 주소 범위만 64비트로 표현하면 되지 않을까? 라는 생각이 듭니다. 하지만 한번에 처리할 수 있는 데이터는 정해져 있기 때문에 연산은 배로 걸리게 되고 성능문제가 생기게 됩니다. 그래서 최적화를 위해 한번에 처리할 수 있는 데이터 크기로 메모리의 주소를 사용하게 된 것 입니다.

 

LLP64/LP64

위 두 데이터 모델은 64비트 시스템에서 정수형 데이터 타입의 크기를 정의합니다. 이 데이터 모델에 따라 운영체제가 정수와 포인터를 다루는 방식이 달라집니다. 

데이터 타입 LP64 (Linux, macOS) LLP64 (Windows)
int 4바이트/32비트 4바이트/32비트
long 8바이트/64비트 4바이트/32비트
long long 8바이트/64비트 8바이트/64비트
pointer 8바이트/64비트 8바이트/64비트

 

위 표에서 보이는 것 처럼 Windows는 LLP64 데이터 모델을 사용하고 있습니다. 이는 Windows 32비트 시스템에 호환되기 위함입니다. 그래서 명시적으로 long을 4바이트로 사용하고 long long을 8바이트로 사용하여 기존 32비트 시스템 코드에서 최대한 문제가 생기지 않도록 하였습니다.

 

그래서 64비트 Windows에서는 주소값을 정수로 변환할 때 반드시 long long 타입을 사용해야 합니다.

 

Windows 자료형

앞서 말했듯 Windows는 64비트/32비트의 호환성을 높이기 위해 시스템에 상관없이 고유한 의미를 지닌 자료형을 표현하게 되었습니다.

Windows 자료형 정의 형태
BOOL typedef int BOOL
DWORD typedef unsigned long DWORD
DWORD32 typedef unsigned int DWORD32
DWORD64 typedef unsigned __int64 DWORD64
INT typedef int INT
INT32 typedef signed int INT32
INT64 typedef signed __int64 INT64
LONG typedef long LONG
LONG32 typedef signed long LONG32
LONG64 typedef signed __int64 LONG64
UINT typedef unsigned int UINT
UINT32 typedef unsigned int UINT32
UINT64 typedef unsigned __int64 UINT64
ULONG typedef unsigned int ULONG
ULONG32 typedef unsigned int ULONG32
ULONG64 typedef unsigned __int64 ULONG64
PINT typedef int* PINT
PINT32 typedef signed int* PINT32
PINT64 typedef signed __int64*PINT64
PLONG typedef LONG* PLONG
PLONG32 typedef signed int* PLONG32
PLONG64 typedef signed __int64* PLONG64
PUINT typedef unsigned int* PUINT
PUINT32 typedef unsigned int* PUINT32
PUINT64 typedef unsigned __int64* PUINT64
PULONG typedef ULONG* PULONG
PULONG32 typedef unsigned int* PULONG32
PULONG64 typedef unsigned __int* PULONG64

 

위 자료형들은 Windows의 시스템에 상관없이 동일하게 표현되며, 32/64비트 시스템에서 모두 사용 가능합니다. 하지만 되도록 64비트 시스템의 자료형을 사용하지 않는것이 권장됩니다. 시스템간의 호환성도 주된 이유지만 많은 코드들이 32비트 시스템 기반 자료형으로 작성됐기 때문에 코드에 익숙해지기를 권장하기 때문입니다.

 

Polymorphic 자료형

유니코드와 아스키코드를 공통 지원하는 매크로 자료형이 있듯이, 32/64비트 시스템을 모두 지원하는 자료형도 있습니다.

#if defined(_WIN64)
    typedef __int64 INT_PTR, *PINT_PTR;
    typedef unsigned __int64 UINT_PTR, *PUINT_PTR;

    typedef __int64 LONG_PTR, *PLONG_PTR;
    typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;

    #define __int3264   __int64

#else
    typedef _W64 int INT_PTR, *PINT_PTR;
    typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;

    typedef _W64 long LONG_PTR, *PLONG_PTR;
    typedef _W64 unsigned long ULONG_PTR, *PULONG_PTR;

    #define __int3264   __int32

#endif

 

위 코드는 Windows.h 헤더에 포함된 basetsd.h 헤더파일 안에 선언된 내용입니다. _WIN64 매크로의 선언유무에 따라 자료형이 바뀌는 것을 확인할 수 있습니다. 즉 비트 시스템에 따라 프로그램이 유동적으로 컴파일이 되어 호환성을 높일 수 있습니다. 그렇기에 사용자가 작성한 함수도 위와 같이 유동적으로 컴파일 될수 있게 작성하는 것이 권장됩니다.

#if defined(_WIN64)
	UINT64 CalDistance(UINT64 a, UINT64 b)
#else
    UINT CalDistance(UINT a, UINT b)
#endif
{
    return a - b;
}

 

위 예시는 상호 호환성을 높이기 위해 권장되는 코드 작성 방법을 나타냅니다.

 

GetLastError

위 함수는 Windows.h 헤더에 선언된 함수로, Windows의 많은 시스템 함수들의 오류 원인을 알 수 있게 에러코드를 반환하는 함수입니다.

DWORD GetLastError(void);

 

함수의 형식은 위와 같습니다. 그럼 GetLastError를 사용한 예제를 작성해 보겠습니다.

#include <iostream>
#include <Windows.h>

int main() {
    HANDLE hFile = CreateFile(
        TEXT("non_existent_file.txt"),
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        DWORD errorCode = GetLastError();
        std::cout << "Error Code: " << errorCode << std::endl;
    }
    else {
        CloseHandle(hFile);
    }
    return 0;
}

 

Error Code: 2

 

위 예시는 존재하지 않는 파일을 열어보아 에러코드 출력하는 코드 입니다. 위 에러코드를 Microsoft Learn에서 검색해보면 "시스템은 지정된 파일을 찾을 수 없습니다."라는 오류 원인을 확인 할 수 있습니다.

 

GetLastError 함수를 사용하면서 주의해야 할 점이 몇 가지 있습니다. 우선 오류가 발생한 직후 실행해야 합니다. 다른 오류가 발생하게 되면 덮어쓰여지기 때문에 확인하려는 오류의 원인을 확인하지 못할 수 있습니다. 그리고 성공시 에러코드를 확인하면 기존 에러코드가 변하지 않기 때문에 잘못된 에러코드를 확인 할 수도 있습니다. 그리고 멀티스레드를 사용중이라면 스레드별로 오류코드를 유지하기에 어떤 스레드인지 식별이 필요합니다.

'개발 > Windows' 카테고리의 다른 글

커널 오브젝트와 오브젝트 핸들  (0) 2025.03.17
Windows 프로세스 생성과 소멸  (0) 2025.03.15
레지스터와 명령어 구조  (0) 2025.03.14
Windows 유니코드  (0) 2025.02.21
Windows 시스템 프로그래밍 시작하기  (0) 2025.02.21