출처 http://bektekk.wo.to/

 

소개

프로그래밍 작업을 하면서 TCHAR, std::string, BSTR등과 같은 많은 문자열관련 데이터타입을 보셨을 겁니다. 또한 _tcs로 시작하는 마크로들도 많이 보셨을 겁니다. 아마 특히 초보분들은 많이들 어려워 하셨을겁니다. 이 강좌는 각각의 문자열 타입을 정리해 보고, 각각의 목적을 소개할 겁니다. 더 나아가 간단한 사용법과 각각의 데이터 타입으로 어떻게 변환할수 있는지도 살펴보겠습니다.

먼저 세가지 종류의 케릭터 인코딩 타입에 대해 구체적으로 알아 보겠습니다. 여러분들은 그 각각의 문자열들이 내부적으로 어떻게 처리되는지 반드시 알아야 할것입니다. 스트링은 캐릭터들의 배열이라는 사실을 이미 알고 계실지라고, 이번강좌는 여러분께 도움이 많이 될것입니다. 또한 이번 강좌를 통해 스트링과 관련된 많은 자료구조(클래스, 구조체)에 대해서도 더 확실히 아시게 될겁니다.

그다음, 스트링 클래스들에 대해 다룰것입니다. 언제 어떤 클래스를 쓰는게 좋은지, 또 각각 어떻게 변환할수 있는지 살펴 볼것입니다.

캐릭터들의 기본 - ASCII, DBCS, Unicode

모든 스트링 클래스들은 사실상 그 근간을 C-스타일 스트링에 두고 있습니다. 다들 아시다 시피 C-스타일 스트링은 캐릭터의 배열로 구성되어 있습니다. 그럼 먼저 캐릭터 타입에 대해 다루도록 하겠습니다. 현재 우리가 쓰고있는 인코딩방법에는 세가지가 있습니다. 그 중 첫째로 single-byte character set, 혹은 SBCS 는 모든 케릭터가 정확히 한바이트를 차지합니다. C의 데이타 타입인 char형을 생각하시면 됩니다. 많이들 알고계실 ASCII 는 SBCS의 가장 대표적인 예입니다. 제로 바이트 즉 '\0' 값이 마지막에 반드시 존재하며, 그것은 문자열의 끝을 나타내게 냅니다.

둘째로는 multi-byte character set, 혹은 MBCS를 들수 있겠습니다. MBCS는 2바이트가 필요한 캐릭터(한글, 일본어, 중국어같은것들)은 2바이트로 1바이트만 써도 되는것들(영어 같은것들)은 1바이트로 표현을 합니다. 사실상 3바이트가 필요한 문자열은 3바이트로 표현을 하겠지만, 그런 문자열들이 지구상에 현재 없죠? 아마도. 윈도우즈에서는 single-byte characters 와 double-byte characters 이렇게 두가지 MBCS 인코딩방식이 쓰입니다. 따라서 윈도우즈에서 지원하는 가장긴 바이트의 캐릭터는 2바이트가 됩니다. 그래서 MBCS는 종종 double-byte character set, 혹은 DBCS 와 같은 의미로 사용되기도 합니다.

DBCS 인코딩방식에서는, 어떤 특정한 값이 2바이트인지를 나타내게 됩니다. 왜냐하면 어떻 케릭터가 1바이트인지 2바이트인지를 구별할수 있는 방법이 필요하기 때문입니다. 예를 들면 Shift-JIS 인코딩에서는 (일본에서 가장 많이 사용되는 인코딩 방식) 0x81-0x9F 와 0xE0-0xFC 사이의 값은 캐릭터가 2바이트라는것을 나타냅니다. 이런 값들을 "lead bytes" 라고 부르고 그 값은 항상 0x7F 보다 큽니다. "lead bytes" 다음에 나오는 바이트는 "trail byte"라고 부릅니다.DBCS에서는 trail byte는 0이 아닌 어떤값을 가질 수 있습니다. SBCS에서와 같이 DBCS방식에서도 '\0' 값을 가지는 한 바이트가 문자열의 마지막을 나타냅니다.

마지막은 Unicode 입니다. Unicode는 모든 캐릭터를 2바이트로 나타내자는 표준 인코딩방식입니다. 유니코드 캐릭터는 종종 wide characters라고도 불리는데요, 이는 1바이트 캐릭터들 즉, SBCS방식보다 더 많은(넓은) 공간을 차지하기 때문입니다. 유니코드는 MBCS와는 다르다는것을 주의 하세요. 가장 큰 차이점을 MBCS방식에서는 한 캐릭터가 1바이트 일수도 2바이트일수도 심지어 3바이트일수도 있습니다. 하지만 유니코드에서는 모든 캐릭터들이 2바이트를 차지하게 됩니다. 또하나의 차이점은 유니코드는 MBCS, SBCS에서와는 다르게 문자열의 끝은 "\0\0" 이런식으로 제로바이트 두개로 표시합니다.

SBCS는 주로 서유럽언어, 대표적으로 영어, 에서 주로 사용됩니고 ASCII표준으로 정의되어있습니다. MBCS는 동아시아 중동지역 언어를 나타내기위해 주로 사용됩니다. (한국, 일본, 중국이 대표적이죠) 유니코드는 COM과 윈도우즈NT에서 내부적으로 사용하고 있습니다.

아마 여러분들은 SBCS 즉, single-byte 캐릭터에는 이미 익숙하실 겁니다. char 타입으로 영문을 사용하실때 이미 여러분은 SBCS를 사용하고 계신겁니다. char타입으로 한글을 사용하신다면 Double-byte 타입 즉, DBCS를 사용하시는 겁니다. 하지만 그와는 다르게 유니코드에서는 wchar_t 타입을 사용합니다. 유니코드 문자열은 C/C++에서 L이라는 문자로 SBCS나 MBCS와는 다르다는 것을 표시해 줍니다.

wchar_t  wch = L'1';      // 2 bytes, 0x0031
wchar_t* wsz = L"Hello"; // 12 bytes, 6 wide characters

캐릭터들이 메모리에 저장되는 방식

1바이트 스트링은 차례차례 1바이트씩 저장이 됩니다. 마지막은 제로바이트 '\0'으로 문자열의 끝을 말해줍니다. 따라서 예를 들어보면"Bob" 이라는 문자열은 이와 같은 방식으로 저장됩니다.

42 6F 62 00

B

o

b

문자열의 끝

유니코드 방식에서, L"Bob"은 이렇게 저장이 됩니다.

42 00 6F 00 62 00 00 00

B

o

b

문자열끝 두개의 제로 바이트

위에서 보시다 시피 캐릭터 0x0000 이 문자열 끝을 나타냅니다.

DBCS 스트링은 겉보기에 SBCS방식과 흡사하지만 그 차이점이 있습니다. 그에 대해서는 뒤로 미루기로 하죠. 문자열 "스트링" 은 아래와 같은 방식으로 저장됩니다.(여기서 LB는 Lead Byte 그리고 TB는 Trail Byte를 뜻합니다.):

93 FA 96 7B 8C EA 00
LB TB LB TB LB TB EOS

문자열 끝

"스"라는 캐릭터는 WORD 값 0xFA93 이런식으로 생각하시면 안됩니다. 두 개의 1바이트값 93FA 의 순서로 "스"라는 캐릭터를 나타내는 겁니다. 따라서 intel계열이 아닌 big-endian 방식이 CPU에서도 그 순서는 같습니다.

스트링 처리함수의 사용

strcpy(), sprintf(), atol()등과 같은 C문자열 처리함수들은 이미 많이 보셨을 겁니다. 중요한 점은 이러한 함수들은 반드시 1바이트 스트링에서만 사용되어져야 한다는 겁니다. 표준라이브러리는 또한가지의 다른 함수셋을 가지고 있습니다. 이 함수들은 유니코드 용인데요, wcscpy(), swprintf(), _wtol() 등의 함수들이 있습니다. 대략 함수중간에 자주보이는 w 는 유니코드를 뜻하죠, wider 캐릭터에서 w를 땃겠죠?

MS는 또한 DBCS를 지원하는 표준라이브러리를 추가했습니다. strXXX()류의 함수는 _mbsXXX()의 함수와 대응됩니다. 만약 여러분의 프로그램이 2바이트 언어권에서 사용된다면 반드시 _mbs로 시작하는 문자열 함수를 사용해야 합니다. 사실 우리 한국사람은 반드시 _mbs류의 함수를 쓰는게 정신건강에 좋겠죠? 왜냐, _mbs함수는 SBCS방식의 문자열도 정확히 처리해 주기 때문입니다. 왜냐하면 MBCS방식에서는 1바이트 캐릭터도 존재하기 때문에 SBCS방식의 문자열이 정확히 처리될수 있는겁니다.

그럼 전형적인 유니코드 스트링을 보면서 왜 여러종류의 스트링 처리함수들이 필요한지 얘기해보겠습니다. 전에 살펴보았던 유니코드 스트링 L"Bob" 입니다.

42 00 6F 00 62 00 00 00

B

o

b

EOS

만약 위의 문자열을 strlen() 함수에 사용하면 어떤 문제가 있을까요? strlen()함수에서는 처음 42값을 가지는 한바이트를 읽고 그 다음 00값의 한바이트를 읽겠죠? 하지만 이 00값의 바이트는 문자열의 끝을 나타냅니다. 따라서 리턴값은 1을 돌려줄 겁니다. 분명 잘못된 결과죠? 음. 반대의 상황은 더욱 치명적입니다. "Bob" 이라는 SBCS방식의 문자열을 wcslen() 함수(유니코드용)에 넘겨준다고 생각해 봅시다. "Bob"는 메모리에 42 6F 62 00 이렇게 저장이 됩니다. 하지만 wcslen() 함수에서는 두바이트씩 읽어 가면서 "0000"이렇게 두바이트가 모두 0인 값을 찾아값니다. "Bob"에 경우 먼저 42 6F를 읽겠죠? 그다음 62 00 을 읽을 것이고 이런식으로 00 00을 찾을때까지 여기저기 들쑤시면서 찾아 나갈 겁니다. 예상과 다른 결과가 나올것은 자명하죠.

strxxx()wcsxxx() 통해 스트링처리함수들에 대해 간략히 얘기해 보았습니다. 그럼 strxxx()_mbsxxx() 의 경우는 어떠할가요? 이 둘의 차이도 정말 중요합니다. 반드시 적절한 방식으로 사용되어 져야 합니다. 이에 대해서는 뒤에서 다루기로 하겠습니다.



스트링을 조작하기^^

우리는 처음 C를 배울때 부터 SBCS 스트링을 사용하는데 익숙해져 있습니다. strlen() 같은 함수는 아마 처음 C공부하실때 누구나 써보셨을 것이고 아직도 쓰시고 계실겁니다. 또한 초보티를 막 벗을때 char* 타입을 이용해 ++, -- 연산자를 이용해 가면서 문자열 조작들을 해봤을겁니다. 또한 [] 식의 배열표현법으로 캐릭터 하나씩 값을 얻어오는 것에도 이미 익숙하실 겁니다. 이런 일련의 작업들은 SBCS 나 Unicode 스트링에서는 훌륭하게 작동합니다. 왜냐하면 모든캐릭터는 같은 길이이기 때문입니다. SBCS에서는 1바이트 유니코드에서는 2바이트 이기 때문이죠.

하지만, 우리는 DBCS 즉, 2바이트이상의 캐릭터들을 사용하게 된다면 이런 습관들을 반드시 버려야 합니다. 만약 DBCS 스트링을 쓸때에는 반드시 따라야 할 두가지의 룰이 있습니다. 이 룰을 어기면 야근이 버그 잡느라 좋아하는 드라마도 못보시고, 야근 하랴, 머리 빠지랴 고생들이 많으실 겁니다. ^^ (너무 장황하게 늘어놓았네요)

첫째, Lead Byte를 채크해지 않을거면 절대 ++ 포인터 연산을 하지말것

둘째. 절대 절대 -- 포인터 연산을 하지 말것

먼저 두번째 룰을 설명하겠습니다. 왜냐하면 설명하기 쉽기때문에^^. 여러분이 설정화일을 사용하는 프로그램을 작성한다고 가정합시다. 실행하면 그 프로그램은 그 설정파일을 읽어서 작업하겟죠? 만약 프로그램의 경로가 C:\Program Files\MyCoolApp이고, 설정파일은 C:\Program Files\MyCoolApp\config.bin 에 있습니다.

그럼 설정파일의 경로를 얻어오는 함수를 이렇게 작성했다고 가정합니다.

bool GetConfigFileName ( char* pszName, size_t nBuffSize )
{
char szConfigFilename[MAX_PATH];

// 인스톨된 디렉토리를 얻어온다.
// .....

// 만약 마지막 캐릭터가 백슬래시가 아니면 추가해 준다.
// 우선 마지막 캐릭터를 구한다.
char* pLastChar = strchr ( szConfigFilename, '\0' );

// *** 자 한칸 앞으로 가자 ***
pLastChar--;

// 백 슬래시 추가
if ( *pLastChar != '\\' )
strcat ( szConfigFilename, "\\" );

// 설정파일 디렉토리 끝에 화일명 추가
strcat ( szConfigFilename, "config.bin"
);

// 리턴
if ( strlen ( szConfigFilename ) >= nBuffSize )
return false;
else
{
strcpy ( pszName, szConfigFilename );
return true;
}
}

이 루틴은 잘짜여졌습니다. 그러나 특정한 DBCS캐릭터에서는 제대로 작동하지 않을 것입니다. 이유는 다음과 같습니다. 만약 디렉토리 이름이 "C:\디렉토리" 와 같다고 가정해 보면. 그 메모리 구조는 다음과 같게 됩니다. :

43 3A 5C 83 88 83 45 83 52 83 5C 00
      LB TB LB TB LB TB LB TB  
C : \

EOS

GetConfigFileName() 함수가 마지막 백슬래시를 확인할때 마지막 0이 아닌 바이트를 확인하는걸 볼수가 있으실 겁니다. (--포인터 연산으로) 그리고 같이 '\\' 인지 == 연산자로 확인했지만 결과적으로는 이 루틴은 잘못된 결과를 리턴합니다.

그럼 무엇이 잘못 되었을가요? 위의 메모리 구조에서 파란색으로 표시한 두 바이트를 살펴보면 백슬래시의 값은 0x5C이고 '리'의 값은 83 5C 입니다. (무언가 눈치 채셨겠죠?.. ^^) 위의 루틴은 '리'의 TB즉 Trail Byte만을 읽고 '\\'와 같은 같으로 간주해 버리게 됩니다.

위의 코드를 수정하려면 --포인터 연산을 제거하고 그에 상응하는 DBCS를 지원하는 함수로 대체 하는 것입니다. 수정한 코드는 아래와 같습니다. :

bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
{
char szConfigFilename[MAX_PATH];

char* pLastChar = _mbschr ( szConfigFilename, '\0' );

// 자 이제 제대로 된다.
// 이전 캐릭터로 이동한다. 이것은 1바이트 일수도 있고
// 2바이트 일수도 있다.

pLastChar = CharPrev ( szConfigFilename, pLastChar );

if ( *pLastChar != '\\' )
_mbscat ( szConfigFilename, "\\" );

_mbscat ( szConfigFilename, "config.bin"
);

if ( _mbslen ( szInstallDir ) >= nBuffSize )
return false;
else
{
_mbscpy ( pszName, szConfigFilename );
return true;
}
}

위의 수정된 루틴은 한 캐릭터 전으로 가기 위해 CharPrev() API 함수를 이용했습니다. 그 한 캐릭터는 위의 예에서와 같이 2바이트를 차지할 수도 있고, 1바이트 일수도 있으나, CharPrev() 함수는 이를 스스로 확인하고 제대로 작동할 것입니다. 따라서 위의 함수는 제대로된 결과 값을 리턴할 것입니다..

이제, 여러분은 위의 1번 규칙을 깨게 될때 일어나는 부작용도 쉽게 생각하실수 있으실 겁니다. 예를 들어, 여러분이 유저가 입력한 파일경로에서 캐릭터 ':' 가 중복되서 나타나는지 아닌지를 확인하는 루틴을 짠다고 생각해 보겠습니다. 그 루틴에서 만약 CharNext() API 함수 대신에 ++ 포인터 연산을 사용하셨다면 잘못된 결과를 초래할 것입니다. 특히 2바이트를 차지하는 한글 한 글자의 Trail Byte가 ':' 와 같게 되는 경우는 100% 잘못된 연산을 하게 될겁니다.

위에서 말한 2번 규칙에 하나를 더하자면:

2a. 절대로 배열 인덱스에 마이너스 연산을 하지 말자.

아래의 코드는 2번 규칙의 예와 굉장히 흡사합니다. 예로, pLastChar 값이 이런식으로 할당되었다면 :

char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];

이 루틴의 결과는 위의 설명드린 --포인터 연산의 부작용예와 똑같은 상황이 벌어집니다. 왜냐 사실 배열 인덱스의 연산도 내부적으로는 포인터 연산으로 처리가 되기 때문이죠.. 헥헥, 꽤나 장황하게 설명된 파트지만 실지 내용은 간단하죠?

다시 strxxx() 와 _mbsxxx() 함수로 돌아가서

이제는 왜 _mbsxxx() 함수시리즈가 필요한지 느끼실 겁니다. strxxx() 계열의 함수는 _mbsxxx() 함수시리즈와는 다르게 DBCS에 대해 전혀 알지 못합니다. 만약 strrchr("C:\\디렉토리", '\\') 이런식으로 호출을 하셨다면 그 결과같은 잘못된 값일겁니다. 반면 _mbsrchr() 함수는 마지막의 2바이트 캐릭터를 제대로 인식하고 제대로된 리턴값을 넘겨줄겁니다.

마지막으로 한가지 추가하자면 문자열 길이를 매개변수로 받거나 리턴하는 함수들에는 주의 하실 필요가 있습니다. strlen("스트링") 의 함수는 6을 리턴할 것입니다. 하지만 유니코드 함수 wcslen(L"스트링") 이건 3을 리턴할 겁니다. 주의하게요^^.

Win32 API에서의 MBCS 와 Unicode

두 종류의 API들

아마 눈치 채셨을지도 모르고, 아닐지도 모르지만, 모든 문자열을 다루는 API들은 두 종류로 이루어져 있습니다. 한가지 버젼은 MBCS를 다루고 다른 하나는 유니코드를 다룹니다. 예로, 실제로는 SetWindowText() 라는 함수는 없습니다. 대신 SetWindowTextA()SetWindowTextW() 라는 두종류의 API가 실제로 존재하는 겁니다. 마지막의 A는 (ANSI의 약어쯤)은 MBCS로 처리하는 함수를 뜻하고, W는 유니코드 버젼을 뜻합니다.

여러분이 프로그램을 빌드 하실때 여러분은 MBCS냐 혹은 유니코드냐를 선택하실 수 있습니다. 만약 VC에서 셋팅을 건드리지 않으셨다면 기본적으로는 MBCS방식으로 빌드될겁니다. 그럼 어떻게 정의되지도 않은 SetWindowText() 라는 함수를 쓸수 있느냐 궁금하시겠죠? winuser.h 헤더파일을 살펴보시면 다음과 같은 일련의 #define문들을 보실 수 있으실 겁니다.:

BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );

#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif

MBCS로 빌드 할때에는 UNICODE 가 정의되 있지 않습니다. 따라서 전처리기는 다음으로 해석하게 됩니다.:

#define SetWindowText  SetWindowTextA

따라서 전처리기는 모든 SetWindowText() 라는 문자를 SetWindowTextA() 라고 바꾸게 됩니다. 실제로 SetWindowText() 라는 매크로로 정의된 다른이름의 함수대신 SetWindowTextA 혹은 W 로 끝나는 함수를 쓰실수 있습니다. 물론 그럴일은 거의 없겠지만요..

그럼 기본값으로 유니코드를 사용하는 함수로 싸그리 바꾸고 싶으시다면, VC설정의 preprocessor settings에서 _MBCS 값을 list of predefined symbols에서 제거해 주시고 UNICODE_UNICODE를 넣어주시면 끝납니다. 주의하실 것은 두개의 값을 모두 적어주셔야 합니다. 어떤해더는 UNICODE라는 것만 사용하고 어떤건 _UNICODE를 사용하기 때문입니다. 그렇지만, 유니코드를 사용하실때 주의 하실게 있습니다. 다음 코드를 살펴보죠:

HWND hwnd = GetSomeWindowHandle();
char szNewText[] = "we love Bob!";

SetWindowText ( hwnd, szNewText );

위 코드는 전처리기가 "SetWindowText"를 "SetWindowTextW"로 바꾼후에는 다음과 같습니다.:

HWND hwnd = GetSomeWindowHandle();
char szNewText[] = "we love Bob!";

SetWindowTextW ( hwnd, szNewText );

무엇이 잘못되었는지 아시겠나요? 여기서 우리는 유니코드를 취하는 함수에 SBCS 즉 1바이트 케릭터를 전달했습니다. 즉 제대로된 결과를 기대할수 없겠죠? 그 첫번째 해결방법은 모든 스트링에 #define문으로 아래와 같이 정의 하는것입니다.:

HWND hwnd = GetSomeWindowHandle();
#ifdef UNICODE
wchar_t szNewText[] = L"we love Bob!";
#else
char szNewText[] = "we love Bob!"
;
#endif

SetWindowText ( hwnd, szNewText );

아마 모든 스트링을 이런식으로 정의하다가는 미쳐 버릴겁니다. 다른 직종을 알아보시겠죠? 여기에 대한 진정한 해결책은 TCHAR 입니다.

고맙다 TCHAR!

TCHAR MBCS에서건 유니코드에서건 똑같은 코드를 사용할수 있게하는 캐릭터 타입입니다. 바로 위에서 본 #define 문들의 해결방식을 이미 MS에서는 정의해 TCHAR 라는 방식으로 정의해 놓았습니다. 아래와 같습니다.:

#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif

따라서 TCHAR 는 MBCS로 빌드할때는 char 이고, 유니코드로 빌드할때는 wchar_t 타입이 됩니다. 또한 유니코드 스트링앞에 추가해 주는 L이라는 문자를 위해 _T() 라는 마크로도 있습니다.:

#ifdef UNICODE
#define _T(x) L##x
#else
#define _T(x) x
#endif

## 는 두개의 문자열들을 하나의 문자열로 만들어 주는 전처리기 연산자 입니다. 여러분은 이 글을 보고 나면 이제 스트링을 선언 할때면 언제든지 _T 마크로를 사용하셔야 합니다. 이 마크로는 유니코드 문자열 앞에는 L문자를 추가해 줍니다.

TCHAR szNewText[] = _T("we love Bob!");

SetWindowTextA/W 를 편하게 왔다갔다 할수 있게 해주는 정의들이 있는것 처럼, strxxx()_mbsxxx() 류의 스트링 처리 함수들 사이를 편하게 변경시킬수 있는 정의 들도 있습니다. 예를 들면, 여러분은 _tcsrchr 마크로를 strrchr() 이나 _mbsrchr() 혹은 wcsrchr()를 쓰는 대신 쓰실수 있습니다. _tcsrchr 함수는 SetWindowText 함수가 그러하듯 MBCS환경이냐 혹은 유니코드 환경이냐에 따라서 적절히 치환될 것입니다.

TCHAR 를 취하는 함수는 비단 strxxx() 계열의 함수만은 아닙니다. 예를 들면 _stprintf (sprintf()swprintf()를 치환) 이나 _tfopen (fopen()_wfopen()를 치환)과 같은 함수들도 있습니다. 이와 관련된 함수들의 전체 리스트는 MSDN의 "Generic-Text Routine Mappings" 부분을 참고하시기 바랍니다.

문자열 그리고 TCHAR 타입들

타입 MBCS환경에서 유니코드 환경에서
WCHAR wchar_t wchar_t
LPSTR 0으로 끝나는 char형의 문자열(char*) 0으로 끝나는 char형의 문자열 (char*)
LPCSTR 0으로 끝나는 const char형의 문자열 (const char*) 0으로 끝나는 const char형의 문자열 (const char*)
LPWSTR 0으로 끝나는 유니코드형의 문자열 (wchar_t*) 0으로 끝나는 유니코드형의 문자열 (wchar_t*)
LPCWSTR 0으로 끝나는 const 유니코드형의 문자열 (const wchar_t*) 0으로 끝나는 const 유니코드형의 문자열 (const wchar_t*)
TCHAR char wchar_t
LPTSTR 0으로 끝나는 TCHAR형의 문자열 (TCHAR* -> char*) 0으로 끝나는 TCHAR형의 문자열 (TCHAR*->wchar_t*)
LPCTSTR 0으로 끝나는 const TCHAR형의 문자열 (const TCHAR*) 0으로 끝나는 const TCHAR형의 문자열 (const TCHAR*)

 

언제 TCHAR 와 유니코드를 사용할 것인가

이제 여러분은 유니코드 없이도 잘 살아 왔는데 왜 유니코드를 사용하여야 하는가 의문이 들 것입니다. 유니코드 방식을 쓰면 득이 될 경우는 세가지가 되겠습니다.:

  1. 프로그램이 오직 Windows NT 환경에서만 돌아간다.
  2. 프로그램이 MAX_PATH 보다 긴 파일명의 문자열을 사용한다.
  3. 프로그램이 Window XP등에서 소개된 오직 유니코드만 받는 함수를 사용한다.

대부분의 유니코드함수는 Windows 9x시리즈에는 구현되 있지 않습니다. 프로그램이 9x시리즈에서도 돌아가야 한다면 유니코드는 좋은 선택이 아닐겁니다. (Microsoft Layer for Unicode 라는 9x에서도 유니코드를 지원하는 라이브러리가 있긴 합니다.) 그렇지만, NT는 내부적으로는 모두 유니코드를 사용하기 때문에 유니코드 API가 아마 약간의 성능향상에 도움이 될것입니다. MBCS방식의 스트링을 NT환경에서 사용하면 운영체제는 내부적으로 그 스트링을 유니코드로 변환한 다음 그에 상응하는 유니코드 함수를 호출할 것입니다. 결과가 리턴되면 다시 MBCS방식으로 전환한 다음 리턴해 줄것입니다. 이 절차는 상당히 최적화 되있겠지만, 아마 유니코드를 직접쓸때보다는 성능의 아주 약간일지도 모르지만 감소는 피할수 없을 겁니다.

NT는 또한 아주 긴 파일명을 지원합니다. (MAX_PATH 보다 긴 파일명) 그러나 유니코드가 쓰일 때만 입니다. 하지만 무엇보다도 가장 큰 장점은 모든 언어 예를 들면 중국어, 영어 , 일어, 한국어 를 똑같은 방식으로 처리해 줄수 있다는 것일 겁니다.

마지막으로, Windows 9x 시리즈를 마지막으로 MS는 MBCS API방식을 멀리하고 유니코드 방식으로 전향하고 있습니다. 예를들면, SetWindowTheme() 이라는 API 함수는 오직 유니코드 스트링 만을 받습니다. 상응하는 MBCS버젼이 없습니다. 유니코드만을 사용하는 것은 이제 유니코드와 MBCS사이의 왔다갔다 해야하는 귀찮음을 얻에 줄겁니다.^^

그리고 지금 당장 유니코드를 사용하지 않으실 지라고, 여러분은 반드시 TCHAR 와 그와 관련된 함수들을 써주셔야 미래를 위해 좋으실 겁니다. DBCS를 보다 안전하게 사용하는것 못지않게 나중에 유니코드로 프로그램을 바꾸실때 단지 셋팅하나 바꿔주면 만사 오케이 이기 때문이죠^^

신고
블로그 이미지

꽃중년

불만있으면 떠나라...

Tag ,

티스토리 툴바