IT 공부/Windows 개발

[Windows] - char과 wchar_t의 차이점 (MBCS와 유니코드)

exdus3156 2024. 12. 21. 23:34

 

#. 일반 문자열과 L유니코드 문자열의 차이

 

 

▷ 우선, 소스코드 자체는 UTF-8로 인코딩되었고, VC 컴파일러 옵션에도 소스코드가 UTF-8로 인코딩되었다고 설정했다. (설정 방법은 여기 링크)

 

표시된 3바이트는 UTF-8로 인코딩된 "한"이다.

 

 실제로 소스코드 텍스트 파일 자체의 바이너리 값을 보면 문자열도 UTF-8로 인코딩되어 있다는 것을 알 수 있다.

 

▷ 그러나 이건 어디까지나 텍스트 파일에 불과한 소스코드를 VC++ 컴파일러가 디코딩하는 차원의 문제이므로, 중요한 것은 빌드되고 난 뒤에 프로그램 내부에 문자열이 어떻게 인코딩되었는지 살펴보는 일이다.

 

 

빌드를 하고 난 뒤 윈도우 실행 파일(PE)의 내부 바이너리 데이터다. 문자열이 UTF-8이 아니라는 것을 쉽게 알 수 있다. 먼저, "hello한글"은 Windows의 로컬 환경인 CP949(MBCS)로 인코딩되어 저장되었다. 그리고 L"hello한글"UTF-16 유니코드로 인코딩되어 저장되었다.

 

그래서 각 문자열이 차지하는 데이터 크기는 먼저 "hello한글"이 10byte다. hello(5) + 한글(4) + null(1).

 

L"hello한글"은 UTF-16이므로 문자 개수(4)에 곱하기 2를 한 값에 null(2)을 더해 16byte가 나온다.

 

 

 

# 결론!

 

Windows에서는 평범하게 큰따옴표만 있는 "" 스타일(char타입)로 문자열을 작성하고 MSVC로 소스코드를 빌드하면 현재 Windows의 로컬 인코딩으로 문자열을 인코딩한다. 한국 Windows는 MBCS 기반인 CP949로 문자열이 인코딩된다.

 

L"" 스타일의 문자열은 Windows에서 빌드 환경과 관련 없이 모두 UTF-16으로 인코딩한다. 즉, 모든 문자를 2바이트로 처리한다.

 

※ 참고) 리눅스에서 빌드하면 char 타입의 따옴표("") 문자열은 UTF-8, wchar_t 타입의 L"" 문자열은 UTF-32로 인코딩된다. UTF-32는 문자 하나를 4byte 고정값으로 쓰는 문자열로, 리눅스 계열에서 개발하는 사람들이 그 존재도 모를 정도로 쓰이지 않는 타입이다. 즉, 리눅스에서는 사실상 char 타입이 문자열 타입이다.

 

 

 

# 왜 플랫폼마다 해석이 다른가?

 

char, wchar_t 모두 C표준이지만, 표준이라고 해서 그것을 어떻게 인코딩할 것인지는 명시하는 건 아니다.

 

그래서 각 플랫폼의 설계자들은 아주 중요한 C언어의 표준 문자 타입을 자체적으로 해석하려고 했다. 윈도우즈는 각국의 플랫폼의 MBCS가 OS의 기본 인코딩 설정이고, 시스템 내부적으로 문자열의 처리는 UTF-16을 선택했으므로 이에 따라 컴파일러(MSVC)를 설계한 것이다. 

 

 

 

#4. 두 문자열 중 어떤 것을 사용해야 하는가?

 

이 결과로 알아야 할 사실은, 내가 char와 "" 문자열로 소스코드를 만들고 이것을 Windows MSVC로 빌드한 결과물은 배포해봤자 똑같은 CP949 기반의 한글 windows에서만 정상적인 동작을 보장할 수 있다는 것이다. 이것이 MBCS 인코딩의 아주 심각한 문제점이다.

 

예를 들어, 문자열을 가지고 처리하는 함수들을 묶어 라이브러리로 배포했다고 가정하자. 그러면 과연 내가 만은 이 라이브러리를 전 세계의 Windows 개발자들이 쉽게 사용할 수 있을까? 문자열을 CP949라고 가정하고 빌드한 이 함수들을...?

 

프로그래밍에서 문자열을 다루지 않을 수도 없는 노릇이다. 따라서 Windows에서 동작하는 프로그램이나 라이브러리 모듈을 개발하고 있고, 각 사용자의 Windows 환경과 상관 없이 일관된 동작을 하도록 만들고 싶다면, 모든 Windows에서 똑같이 동작하는 유니코드 기반의 wchar_t 타입과 L문자열을 사용해야 한다.

 

이렇게 유니코드 기반의 문자열과 Windows API를 사용하는 것이 성능에도 좋다. 애시당초 Windows API는 이미 예전부터 유니코드 UTF-16으로 커널과 시스템 API가 동작했으며, printf()와 같이 char 기반의 레거시 표준 라이브러리 코드도 내부적으로 MBCS -> Unicode(UTF-16)으로 변환되는 로직이 있으므로 성능이 약간 불리하다. Windows API를 char 기반으로 처리할 수도 있고, Windows가 그걸 지원해주기도 하지만 Windows API를 사용할 정도의 프로젝트에서 굳이 그런 식으로 번거롭게 개발할 이유가 없다.

 

게다가 평생 한국에서만 동작하는 소프트웨어만을 개발하고 배포할 것도 아니고, 그냥 처음부터 전 세계의 모든 문자열을 처리할 수 있도록 프로그램이나 라이브러리를 개발하는 것이 낫다. char와 "" 같은 레거시 기반의 소스코드를 만들어 빌드하고 배포하면 나중에 혹시나 다국어 지원이 필요하거나 전 세계에 배포할 때, 고리타분하게 char를 쓴 과거의 자신을 후회하지 않을까?