요새 한참 XML parser를 만들기 위해 이것 저것 공부도 하고 코딩도 하고 있는 중입니다. 그런데 역시 아는 것과 하는 것 사이에는 많은 차이가 있군요. Devils in the details랄까요? ;-)
이번 글에서는 char 타입과 EOF에 관한 내용에 대해 생각해 볼까 합니다. 다들 아시다시피 기존의 8bits char 타입을 사용하는 C 함수들의 경우 char를 리턴하기 위해 int 타입을 사용하였습니다.
이유는 하나, 실제 사용되는 char 값들과 EOF값을 구분하기 위해서였죠. 8bits char 타입의 값들은 char가 unsigned라고 가정하면 ((char나 wchar_t의 signed, unsigned 여부는 구현에 따라 다를 수 있습니다.)) 0~255까지의 범위안의 값들만 가질 수 있기 때문에 256부터의 값은 어떤 것이든 EOF를 나타내기 위해 사용될 수 있습니다. 일반적으로 -1, 즉 0xFFFFFFFF이 사용되죠.
그런데 이번에 XML parser를 구현하면서 평소에 잘 사용하지 않던 wchar_t 타입을 사용하다 보니 궁금한 점이 생겼습니다. Linux의 g++에서 wchar_t의 sizeof는 32bits입니다. 그렇다면 WEOF의 타입은 무엇이어야 할까요? 또 값은요. 만약 wchar_t의 값이 0~0xFFFFFFFF을 모두 사용할 수 있다고 생각한다면 ((wchar_t가 unsigned라고 가정했을 경우)) WEOF의 타입은 uint64_t 정도가 되어야 할 것입니다. 하지만 64bits int 타입을 아직 모든 컴파일러가 지원하는 것은 아니므로 문제가 될 수 있습니다.
그래서 아래와 같이 간단한 코드를 사용하여 시험해 보았습니다.
예상대로 WEOF의 타입은 uint32_t쯤 되는 것 같습니다. ((실제 Linux에서 헤더 파일들을 찾아 들어가보니 stream 클래스에서 사용하는 eof()의 타입은 int로 정의되어 있네요.)) 그렇다면 어떻게 32bits 타입의 EOF를 32bits char 타입에 사용할 수 있을까요? 이유는 과거에 사용되었던 문자들, 현재 사용되는 모든 문자들을 다 더해도 이렇게 많지 않다고 가정할 수 있기 때문입니다. Unicode만 해도 최대 가질 수 있는 값은 0x10FFFF까지니까 안전하게 (uint32_t) -1 값을 WEOF로 사용해도 문제가 없습니다.
아마 우주의 다른 곳에서 외계인들이 발견되고 그 외계인들에게 우리가 만든 프로그램을 팔아야 되기 전까지는 안전하게 32bits짜리 WEOF를 사용할 수 있을 것 같습니다. :-)
이번 글에서는 char 타입과 EOF에 관한 내용에 대해 생각해 볼까 합니다. 다들 아시다시피 기존의 8bits char 타입을 사용하는 C 함수들의 경우 char를 리턴하기 위해 int 타입을 사용하였습니다.
ex) int fgetc(FILE *stream);
이유는 하나, 실제 사용되는 char 값들과 EOF값을 구분하기 위해서였죠. 8bits char 타입의 값들은 char가 unsigned라고 가정하면 ((char나 wchar_t의 signed, unsigned 여부는 구현에 따라 다를 수 있습니다.)) 0~255까지의 범위안의 값들만 가질 수 있기 때문에 256부터의 값은 어떤 것이든 EOF를 나타내기 위해 사용될 수 있습니다. 일반적으로 -1, 즉 0xFFFFFFFF이 사용되죠.
그런데 이번에 XML parser를 구현하면서 평소에 잘 사용하지 않던 wchar_t 타입을 사용하다 보니 궁금한 점이 생겼습니다. Linux의 g++에서 wchar_t의 sizeof는 32bits입니다. 그렇다면 WEOF의 타입은 무엇이어야 할까요? 또 값은요. 만약 wchar_t의 값이 0~0xFFFFFFFF을 모두 사용할 수 있다고 생각한다면 ((wchar_t가 unsigned라고 가정했을 경우)) WEOF의 타입은 uint64_t 정도가 되어야 할 것입니다. 하지만 64bits int 타입을 아직 모든 컴파일러가 지원하는 것은 아니므로 문제가 될 수 있습니다.
그래서 아래와 같이 간단한 코드를 사용하여 시험해 보았습니다.
cout << sizeof(char) << ", " << sizeof(EOF) << ", " << EOF << endl;
cout << sizeof(wchar_t) << ", " << sizeof(WEOF) << ", " << WEOF << endl;
// result
// 1, 4, -1
// 4, 4, 4294967295(-1)
예상대로 WEOF의 타입은 uint32_t쯤 되는 것 같습니다. ((실제 Linux에서 헤더 파일들을 찾아 들어가보니 stream 클래스에서 사용하는 eof()의 타입은 int로 정의되어 있네요.)) 그렇다면 어떻게 32bits 타입의 EOF를 32bits char 타입에 사용할 수 있을까요? 이유는 과거에 사용되었던 문자들, 현재 사용되는 모든 문자들을 다 더해도 이렇게 많지 않다고 가정할 수 있기 때문입니다. Unicode만 해도 최대 가질 수 있는 값은 0x10FFFF까지니까 안전하게 (uint32_t) -1 값을 WEOF로 사용해도 문제가 없습니다.
아마 우주의 다른 곳에서 외계인들이 발견되고 그 외계인들에게 우리가 만든 프로그램을 팔아야 되기 전까지는 안전하게 32bits짜리 WEOF를 사용할 수 있을 것 같습니다. :-)
char이 담을 수 있는 값 + EOF를 담을 수 있는 형이 int라면, wchar_t가 담을 수 있는 값 + WEOF를 담을 수 있는 형은 cwchar에 정의된 wint_t입니다. 이 자료형은 ISO C의 7.24.1절에 설명되어 있고, ISO C++에서 이를 그대로 받아들여 쓰고 있지요. :)
ReplyDelete아.. 그렇군요. 시험만 해볼께 아니라 문서를 찾아봤어야 했는데... :-) C99 문서의 7.24.1를 읽어보니 추측했던 내용들이 대충 맞군요.
ReplyDeleteWEOF - which expands to a constant expression of type wint_t whose value does not correspond to any member of the extended character set.
그리고 footnote 269, 270번을 보니 위의 시험 결과가 C99 표준을 준수하고 있다는 것을 알 수 있겠네요.
269) wchar_t and wint_t can be the same integer type.
270) The value of the macro WEOF may differ from that of EOF and need not be negative.