1.
c언어는 문자를 표현하기 위해서, 사용하는 규약을 아스키 코드만으로 제한을 해둔 것인지 아니면 어떤 규약이라도 사용될 수 있도록 하여 컴파일러나 운영체제가 그 중에서 하나(아스키 코드)를 사용하는 것인지가 궁금합니다.
예를들어 문자 A를 10, 11, 12로 할당해둔 3개의 인코딩 방식이 있다면 C언어는 이 중 하나만을 사용하기로 제한 해두어서 C컴파일러는 오직 그 하나만의 인코딩 방식만을 지원하는 것인지 아니면 C언어가 어떤 인코딩 방식이라도 사용할 수 있도록 하여 C컴파일러가 선택할 수 있는 규약중 하나(아스키 코드)를 사용하는 것인지가 궁금합니다.
2.
아스키 코드 문자가 아닌 문자 예를들어 한글 같은 경우처럼 자국의 문자를 표현하기 위한 규약과 관련해서는 c언어는 어떻게 명시하고 있을까요? c언어 입장에서는 어떤 나라의 어떤 문자를 표현해야 될지 알고 어떤식으로 명시하고 있을지가 궁금합니다.
예를들어 아스키 문자가 아닌 한국에서 사용하는 문자라던가, 중국에서 사용하는 문자 그리고 일본에서 사용하는 문자의 경우처럼 이를 표현하기 위해 C언어는 어떤식으로 명시하고 있는지가 궁금합니다. C언어 입장에서 보면 어떤 국가의 어떤 문자가 있는지도 모를텐데 어떻게 하라고 명시하고 있을지가 궁금한 것입니다.
보통 ASCII 인코딩을 사용하지만, 반드시 그렇게 해야 한다는 제약은 없습니다.
locale(언어 환경) 설정을 따릅니다. locale 설정에 따라 표기되는 문자가 다릅니다. \는 한국어 locale에서는 원화 표시, 일본어 locale에서는 엔화 표시가 됩니다.
멀티바이트 문자는 다루기 어려운 문제입니다. wchar_t 타입이 있지만, 이것도 완전하지 않습니다.
사실상 다루기 어렵다고 보면 됩니다.
Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference
https://goo.gl/VNo1TV
전문적인 내용은 이 책을 봐야 한다고 알고 있습니다.
국제화 프로그래밍이고, 이것만 전문적으로 담당하는 분들이 있습니다.
ICU라고 IBM이 만들어 오픈 소스로 공개한 국제화 라이브러리가 있습니다.
http://icu-project.org/apiref/icu4c/
유명한 게임 제작사들도 대부분 다국어 지원을 위해 ICU를 사용하고 있다고 알려져 있습니다.
페이지 밑에 보시면 구글 크롬, 맥 OS X, XBox 등 대부분이 ICU를 사용합니다. C/C++ 언어만으로는 해결하기 어려운 것 같습니다.
C 언어 표준안 5.2.1에서 환경에 대한 고려에는 다음과 같은 문자를 쓸 수 있고, 5.2.1.2에는 멀티바이트 문자도 쓸 수 있다고 되어 있습니다.
문자는 다음 문자를 쓸 수 있고, 1바이트 크기에 들어갈 수 있어야 합니다.
5.2.1 Character sets
Both the basic source and basic execution character sets shall have the following
members: the 26 uppercase letters of the Latin alphabet
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
the 26 lowercase letters of the Latin alphabet
a b c d e f g h i j k l m
n o p q r s t u v w x y z
the 10 decimal digits
0 1 2 3 4 5 6 7 8 9
the following 29 graphic characters
! " # % & ' ( ) * + , - . / :
; < = > ? [ \ ] ^ _ { | } ~
the space character, and control characters representing horizontal tab, vertical tab, and
form feed. The representation of each member of the source and execution basic
character sets shall fit in a byte.
C 언어가 문자를 자동으로 인지하고 처리한다거나 하는 건 아닙니다.
아랍어 문자를 알 수는 없겠지요. 그래서 ICU 같은 다국어 처리 라이브러리를 사용한다고 알고 있습니다.
다국어 처리에 대해 알고 싶다면 앞서 소개한 <Standard C++ IOStreams and Locales>뿐이라고 알고 있습니다.
감사합니다.
24.3 Differences between the C Locale and the C++ Locales
http://stdcxx.apache.org/doc/stdlibug/24-3.html이 글도 참고가 될 것 같습니다.
많이 늦었죠..^^? 도움주셔서 감사하다는 말씀을 이제서야 남기네요ㅠㅠ 사실 비로그인으로 거의 매일 접속은 했다만.. 저 스스로 만족할만한 이해를 완성하지 못해서 이제서야 감사하다는 말씀 드립니다! 이렇게 오래 걸릴줄은 몰랐어요..T.T 다음 내용은 전웅 저자의 C 펀더멘탈 이라는 책에서 발췌한 내용이 포함됨을 미리 말씀드립니다!
1) C언어의 기반이 되는 문자세트로 ASCII가 될 수 없는 이유는?
C언어는 다른 프로그래밍 언어에 비해 매우 다양한 '그래픽 문자(제어문자 이외의 문자)'를 사용합니다. 이는 기본적으로 C언어가 태어난 환경이 다양한 그래픽 문자를 지원하는 ASCII를 사용하던 시스템이기 때문입니다. 하지만 국제 표준을 갖게 될 C언어의 기반이 되는 문자세트를 ASCII로 할 수는 없었습니다. 왜냐하면 ASCII를 기반으로 한 ISO 646 IRV(International Reference Version)에서는 ASCII 문자세트 중 자주 사용되지 않는 그래픽 문자 12개(#, $, @, [, \, ], ^, `, {, |, }, ~)에 대해서는 필요에 따라 각 국가별로 자신들만의 문자를 할당할 수 있도록 국가별 ASCII 변종을 정의하여 사용할 수 있게 하였는데, 이러한 이유 탓에 국제 표준을 갖게 될 C언어를 ASCII에 의존시키는 것이 바람직하지 않았습니다.
2) C언어의 기반이 되는 문자세트는?
그래서 ASCII 대신 C언어를 기술할 수 있는 문자세트로 선정된 것이 ISO 646 Invariant Set입니다. 이는 ISO 646 IRV가 정의하는 여러 ASCII의 변종들 중 변화가 없는 부분만 모아놓은 문자세트를 말합니다. 즉 ASCII에서 자주 사용되지 않는 그래픽 문자 12개를 제외한 ASCII를 Invariant Set이라 할 수 있습니다. 때문에 Invariant Set은 당연히 ISO 646 IRV 즉 ASCII의 부분집합입니다.
3) Trigraph Sequence가 필요한 이유는?
C언어가 기반으로 하고 있는 문자세트가 ISO 646 Invariant Set이므로, C언어를 지원하는 환경이라면 Invariant Set을 기반으로한 코드세트(문자세트와 더불어 각 문자에 할당된 정수값)를 사용할 수 있지만, 정의되지 않은 그래픽 문자 9개(ISO 646 Invariant Set은 C언어에서 사용되는 모든 문자를 담고 있지 않습니다)는 다른 표현방법이 필요한데 이를 위해서 C표준에는 Trigraph Sequence를 도입하게 됩니다.
4) C언어를 지원하는 환경이 ASCII를 사용해도 되는 이유는?
"C언어의 기반이 되는 문자세트"라는 표현에 대해서 절대로 잊어서는 안 되는 중요한 것이 있는데, C표준이 ISO 646 Invariant Set을 기반 문자세트로 두었다는 이야기는 C가 사용하는 문자들만을 그 문자세트에서 빌려왔다는 것입니다. 따라서 C언어를 지원해주는 모든 환경이 대조열(문자에 할당된 정수값)까지 동일한 문자세트를 사용해야 한다는 것을 의미하지는 않습니다. 다만 어떤 문자가 어떤 정수값을 갖는지 상관없이(대조열과 상관없이) 최소한 ISO 646 Invariant Set에 있는 문자들을 모두 가지고 있는 어떠한 문자세트도 C언어 표준을 만족하는 환경이 될 수 있다는 것을 의미합니다.
5) ASCII는 256개의 문자이다?
ASCII는 기본적으로 정보 교환을 위해 1963년에 개발된 미국 표준 문자세트입니다. ASCII는 7비트 코드를 사용하므로 (즉 7자리의 이진 숫자로 구성된 코드값을 갖습니다) 2^7=128개의 다른 문자를 표현할 수 있습니다. 0번부터 127번에 해당하는 128개의 문자 중 94개(33번~126번)가 화면에 출력되어 문자를 보여줄 의도로 정의된 인쇄문자이며, 32번(space)과 127번(delete)을 제외한 나머지 32개(0번~31번)는 다양한 용도로 사용되는 제어문자입니다. ASCII는 기본적으로 영어를 표현하기 위한 의도로 고안된 세트이지만, 영어와 그 기원을 같이하는 유럽의 다른 언어들에 대해서도 문제 없이 쓸 수 있었습니다. 하지만 안타깝게도 ASCII를 2^8=256개의 문자를 갖는 8비트 코드값을 사용하는 문자세트로 오해하고 있는 사람들이 있습니다. 이는 잘못된 것입니다. 현재의 많은 컴퓨터들이 문자 하나를 표현하기 위해 8비트의 공간을 할당하고 있는 것은 사실이지만, 표준으로서 ASCII는 여전히 7비트 코드를 사용하며, 남은 1비트 공간에 의해 128번부터 255번까지의 128개의 공간이 비어 있습니다(쓰에지 않습니다). 이렇게 주인 없이 비어있는 공간에 각 국가나 컴퓨터 산업체들이 자신들의 필요한 여러 문자를 배치하여 사용하기 시작했고, 이런 관행때문에 언젠가부터 ASCII가 0번부터 255번까지의 코드값을 갖는 것으로 알려졌습니다. 사실 이렇게 ASCII를 확장하여 사용하는 행위가 잘못된 것은 아닙니다. 필요에 의해 확장된 ASCII를 비공식적으로는 'ASCII-8' 혹은 '확장 ASCII(Extended ASCII)'라고 합니다. 중요한 것은 ASCII-8을 사용하는 시스템이라고 해도 표준인 ASCII가 정의해주는 것은 127번까지 이므로 128번 이후부터는 같은 코드값에 대해 각각 다른 종류의 문자가 정의될 수 있습니다. 따라서 일부 잘못된 ASCII에 대한 소개만 믿고, 그 이후 문자에 대해서도 동일한 의미가 보장될 것이라 생각한다면 큰 실수입니다(코드값 128번 이후의 문자가 서로 다를 수 있는 확장 ASCII의 예로 IBM-PC ASCII와 MS 윈도우 ASCII의 차이를 확인해 보시면 됩니다).
6) 크로스 컴파일러가 필요한(번역환경과 실행환경을 구분하는) 이유는?
void 임의로 가정한 상황(void)
{
새로 개발된 시스템이 도착했다. CPU를 포함한 모든 부분이 새로 개발된 것이기에 기존의 어떤 운영체제와도 호환되지 않습니다. 여러분에게 주어진 것은 오직 시스템 하드웨어에 대한 자세한 설명서와 원시적인 그 시스템만의 운영체제 그리고 장착된 CPU에서 사용할 수 있는 어셈블리어와 어셈블러 뿐이다. 여러분이 맡은 일은 많은 응용 프로그램을 개발하기 위해 그 시스템에서 작동하는 C컴파일러를 만드는 것이다. 어떻게 할 것인가? 물론 여러분은 새로운 어셈블리어를 쉽게 익힐 수 있으며 어셈블리 언어나 C언어로 C컴파일러를 만들 수 있다고 가정한다.
}
가장 고되지만 단순하게 생각할 수 있는 방법은 그 시스템에서 프로그램을 개발할 수 있는 유일한 도구는 시스템과 함께 제공된 어셈블리어 뿐이므로 빠른 시간 내에 그 어셈블리어를 익혀서 C컴파일러를 만들면 됩니다. 또는 어셈블리어로 가장 단순한 형태의 C컴파일러를 만들고(단순한 형태의 초라한 C언어도 만들고) 초라한 C언어를 사용해 좀 더 복잡한 기능을 지원하는 또 다른 C컴파일러를 만드는 과정(좀 더 복잡한 기능을 지원하는 C언어도 만드는 과정)을 반복하여 최종적으로 원하는 수준의 작품을 얻어낼 수 있을 것입니다.
하지만 좀 더 현명한 방법은 익숙한 개발환경을 제공하는 시스템에서 새로운 시스템을 위한 C컴파일러를 만드는 것입니다. 새로운 C컴파일러를 기존의 환경에서 만들되, 새로 제작되는 C컴파일러가 출력하는 결과를 기존 환경이 아닌 새 환경에서 사용할 수 있는 새 시스템의 어셈블리어로 나오게 하면 됩니다. 혹은 이미 프로그램 소스를 가지고 있는 C컴파일러가 있다면 그 컴파일러의 결과 출력 부분만을 새 시스템의 어셈블리어로 바꾸면 되므로 문제가 더 간단히 해결될 수도 있습니다. 이렇게 제작된 새 컴파일러는 컴파일 작업 자체는 기존 환경에서 하지만, 컴파일의 결과물은 다른 시스템에서 실행될 수 있는 '크로스 컴파일러'가 되는 것입니다. 또한 크로스 컴파일러에 의해 수행되는 컴파일 작업을 '크로스 컴파일'이라고 합니다. 크로스 컴파일과 같이 프로그램을 컴파일하여 결과를 얻어내는 개발환경과 실제 그 프로그램을 사용하는 사용환경이 별개로 존재하는데, 이를 각각 '번역환경'과 '실행환경'으로 구분합니다. 즉 번역과 실행을 하나의 환경에서 한다는 것은 번역환경과 실행환경이 하나로 일치되는 경우일 때만 가능하며 C언어는 크로스 컴파일을 위해서라도(C표준은 크로스 컴파일을 배려합니다) 번역환경과 실행환경을 구분해야 합니다.
7) 소스 문자세트와 실행 문자세트가 달라질 수 있는 이유는?
C 언어는 크로스 컴파일을 배려하는 언어로 프로그램이 번역되는 번역환경과 번역된 프로그램이 실행되는 실행환경에 구분을 둔다고 이야기 했습니다. 물론 이 두 개의 환경은 C언어가 요구하는 번역과 실행을 위한 최소한의 요구를 만족한다는 점을 제외하면 더 이상의 공통점을 가질 필요가 없습니다. 이는 각 환경이 사용하고 있는 문자세트에도 그대로 적용됩니다. 따라서 각 환경에서 사용하는 문자세트도(같으면 상관이 없겠지만 다를 경우를 위해) 별도로 이름을 붙여 구분할 필요가 있습니다. 이에 번역환경에서 프로그램 소스를 작성하는데 쓰이는 문자세트를 '소스 문자세트', 실행환경에서 프로그램이 실행 중에 입출력에 사용하는 문자세트를 '실행 문자세트'라고 합니다. 물론, 번역환경과 실행환경이 동일한 대부분의 경우에는 소스 문자세트와 실행 문자세트 역시 동일할 가능성이 큽니다(항상 동일하다는 보장은 없습니다).
8) 소스 문자세트와 실행 문자세트에 대한 C언어의 최소한의 요구사항은?
다음은 별개의 문자세트일 수 있는 이 두 개의 문자세트에 적용되는 표준의 요구사항 입니다.
첫째, 두 문자세트가 동일하게 가지고 있어야 하는 문자들
- 알파벳 대소문자 52개
- 10개의 십진 숫자를 표시하는 문자
- 29개의 그래픽 문자
- 공백(공백과 공백문자는 서로 다른 개념)
- 수평탭
- 수직탭
- 폼피드
둘째, 소스 문자세트가 가지고 있어야 하는 문자들
- 한 줄의 끝을 표시하는 문자(End-Of-Line). 사실상 셋째에서의 개행문자와 동일하다고 생각할 수 있습니다.
셋째, 실행 문자세트가 가지고 있어야 하는 문자들
- 경보를 표시하는 제어문자
- 백스페이스
- 캐리지 리턴
- 개행문자
- 널문자
다음은 기억해야 할 몇 가지 주의사항 입니다.
첫째, 십진 숫자를 표현하는 문자(0부터 9)는 순차적으로 증가하는 코드값을 가져야 합니다.
- 0이라는 문자가 어떤 코드세트에서 48이라는 코드값을 갖는다면 이 코드세트에서 1부터 9까지의 문자가 각각 49부터 57까지의 코드값을 가져야만 표준이 요구하는 바를 만족시킨다는 뜻입니다.
둘째, 표준은 위에서 언급한 문자들의 모양까지 요구하지는 않습니다.
- 한글이 지원되는 대부분의 시스템에서 키보드를 통해 입력된 \(역슬래시) 문자는 원화로 나타나며, 일본 시스템에서는 엔화로 나타납니다. 중요한 것은 그것들이 모양에 상관없이 일관되게 \(역슬래시)를 의미하며, 다른 문자들과 구분된다는 점입니다.
셋째, 위에서 언급한 문자들은 표준이 요구하는 최소한의 문자들이지 이 이상의 문자들을 사용하지 말라는 뜻은 아닙니다.
- ASCII는 C언어가 요구하는 모든 문자들을 모두 포함하고도 추가로 여러 개의 제어문자와 그래픽 문자를 가지고 있습니다.
넷째, 십진 숫자를 표시하는 문자를 제외하고 다른 문자들의 순서는 따로 보장될 필요가 없습니다.
- ASCII의 폐해가 가장 크게 드러나는 부분입니다. ASCII에서는 영문 대문자와 영문 소문자가 순서대로 연속 배치되어 있습니다. 그 덕분에(?) 많은 사람들이 모든 환경에서도 마찬가지일 것이라 가정합니다. 그러나 이는 틀린 가정입니다. IBM 컴퓨터에서 종종 사용되는 EBCDIC 문자세트는 영문 대소문자가 연속적으로 나오지 않습니다(I와 J, R과 S).
9) C 표준에서 말하는 바이트와 문자란?
바이트는 실행환경 문자세트의 문자 중 임의의 한 문자를 저장할 수 있을만큼의 크기(즉 비트 개수)를 갖는 메모리의 단위입니다(번역환경이 아닌 실행환경 문자세트라고 하고있다).
문자는 한 바이트의 크기(즉 바이트의 정의에 의해 요구되는 비트 개수)에 맞는 임의의 비트열입니다(여기서 말하는 문자는 실행환경의 문자세트가 아닐까라는 추측).
참고로 C언어에서 사용하는 "문자"라는 용어에는 두 가지 개념이 중첩되어 있습니다. 하나는 우리가 직관적으로 이해하고 있는 일상적인 문자의 개념이고, 다른 하나는 위에서 정의하고 있는 문자의 개념입니다. 따라서 C언어에서 사용되는 "문자"라는 용어는 문맥에 따라 이 두 개념 중 하나를 선택하여 이해해야 합니다.
10) 바이트에 대한 추가적인 제한은?
하나는 바이트의 크기가 "최소한" 8비트 이상이어야 하며, 다른 하나는 각 바이트는 메모리에서 고유한 주소값을 가져야 한다는 것입니다.
- 우선 바이트의 크기는 실행 문자세트가 얼마나 많은 문자를 가지고 있느냐에 따라 그 최종적인 크기가 결정됩니다. 많은 사람들은 바이트를 8비트를 묶어 부르는 용어로 이해하고 있습니다. 이는 대부분의 사람이 잘못 알고 있는 지식입니다. 절대 바이트는 8비트를 묶어 부르는 용어가 아닙니다(정확히 8비트를 부르는 용어로 '옥텟'이 있습니다). 다만 해당 컴퓨터에서 한 문자를 저장하기에 충분할 만큼의 공간을 이야기하는 것으로, 요즘 사용하는 대부분의 컴퓨터가 우연히 8비트를 사용하고 있을 뿐입니다. 실제 비록 소수이지만, 1바이트를 9비트나 16비트로 정의하는 컴퓨터도 있습니다. 따라서 만약 실행 문자세트의 총 문자 개수가 512개라면 당연히 한 바이트의 크기는 "최소" 9비트가 될 것입니다.
- C에서 한 바이트의 크기는 최소 8비트가 되어야 합니다. 즉 한 바이트로 7비트를 사용하는 컴퓨터들은 C언어를 제대로 지원하지 못하거나 혹은 (지원하기 위해서) 편법을 사용해 C프로그램의 입장에서는 1바이트가 8비트 이상인 것처럼 보이도록 만들어야 합니다.
- 각각의 바이트는 메모리에서 별도의 주소값으로 접근될 수 있어야 합니다. 즉 컴퓨터가 메모리의 위치를 가리키는 어떤 주소값을 사용하여 메모리에 접근할 때 가장 최소가 되는 단위가 바이트여야 합니다. 물론 대부분의 컴퓨터가 바이트 기반의 주소 체계를 사용하는 요즘에는 이러한 사실이 당연한 것으로 생각될 수 있지만, (바이트보다 일반적으로 크기가 큰) 워드 단위로의 접근만 허락하는 워드 기반의 주소 체계를 사용하는 일부 컴퓨터에서는 골치 아픈 편법을 사용해야 C언어를 올바르게 지원할 수 있음을 의미합니다.
- 이제 C언어에서 기억장치를 다루는 가장 기본적인 단위는 바이트가 됩니다. C에서 다루는 바이트보다 큰 모든 단위는 바이트의 정수배 크기를 갖는것으로 정의됩니다.
11) 문자에 대한 추가적인 주의사항은?
간단히 한 바이트에 저장할 수 있는 비트열을 문자라고 합니다. 즉 한 바이트가 9비트로 구성되어 있다면 코드값 0을 갖는 문자부터 코드값 511을 갖는 문자까지 총 512개의 문자가 있습니다. 한 바이트에 저장할 수 있는 수치값은 모두 어떤 문자의(문자세트에 해당 코드값을 갖는 문자가 실제로 존재하든 아니든) 코드값이 되고, 우리는 그것을 "문자"라고 합니다.
- C언어를 지원하는 모든 환경이 반드시 포함하고 있어야 하는 문자들은 반드시 한 바이트에 들어갈 수 있는 크기의 코드값을 가져야 합니다(실행 문자세트 뿐만 아니라 번역 문자세트까지도). 즉 만약 바이트가 8비트인 경우, C언어가 요구하는 문자 중 하나인 #가 2500이라는 코드값을 가져서는 안 됩니다.
12) 소스코드 내에서 다양한 언어가 사용 가능한 이유는?
C99는 프로그램 소스 내에서 직접 'UCN(Universal Character Name)'을 사용할 수 있도록 새로운 표기법을 도입하였습니다. 이를 이용하면 프로그램에 명칭, 문자(열) 상수, 주석등에 영어 외에 원하는 문자를 자유롭게 써넣을 수 있습니다. 또한 UCN이 지칭하는 문자는 모두(일반적으로 Unicode)에 의해 정의된 것이기에 영어가 아닌 문자가 포함되어 있는 프로그램도 UCN을 지원하는 모든 임플리멘테이션에서 이식성을 그대로 유지할 수 있습니다.
13) 간단하게 ASCII 코드에 해당하는 문자를 알아보는 방법은?
명령 프롬프트를 실행하고 왼쪽 Alt를 누른 상태에서 키보드의 오른쪽에 있는 숫자 키패드에서 숫자를 입력하면 됩니다. 예를 들면 왼쪽 Alt를 누르고 48을 입력하면 화면에 0이 출력되고, 65를 입력하면 화면에 A가 출력됩니다. 참고로 코드값 입력이 완료되면 누르고 있던 Alt는 떼야합니다.
문자에 대해서 기본적인 내용을 최종적으로 작성해보았습니다. C언어 코딩 도장, C언어 펀더멘탈 두 책을 참고하였으며, 기타 여러 웹사이트 내용을 참고하였습니다.
1) 레퍼토리(repertoire)란?
폼피드, 수직탭 같은 제어문자를 제외한 우리가 구분할 수 있는 ‘인쇄문자’의 집합을 의미합니다. 따라서 영어={A, B, C, ...}, 한글={가, 나, 다, ...}의 레퍼토리를 가질 수 있을 것입니다. 참고로 집합은 순서가 없으므로 레퍼토리는 단순히 인쇄문자들을“순서없이” 모아놓은 것으로 볼 수 있습니다.
2) 대조열을 이해하기 위해 먼저 살펴보는 ASCII의 관찰 결과는?
ASCII를 주의 깊게 관찰해보면 아시겠지만, 7비트 크기의 ASCII는 상위 3비트가 표현할 문자의 종류(그룹)를 결정 짓고, 나머지 하위 4비트가 해당 그룹에서 표현할 문자의 개수를 결정합니다. 다음은 상위 3비트가 10진수로 몇 인가에 따른 관찰의 결과입니다.
0~1: 제어문자(두 개의 그룹이므로 총 32개)
2: 특수문자(한 개의 그룹이므로 총 16개)
3: 숫자문자 및 특수문자
4~5: 대문자 및 특수문자
6~7: 소문자 및 특수문자
3) 대조열(collating sequence)이란?
사람이 사용하는 문자와 컴퓨터가 내부적으로 인식하는 숫자 사이의 함수 관계를 의미합니다. 물론 숫자와의 함수 관계이므로 당연히 문자들 사이의 어떤 순서가 자동적으로 정의됩니다. 앞서 살펴본 ASCII의 관찰 결과에 근거하면 상위 3비트만 대조해 보아도 소문자 보다는 대문자가, 대문자 보다는 숫자문자가 순서상으로 앞서는 것을 알 수 있는데, 이를 대조열이라 하지 않나 싶습니다. 이처럼 대조열은 레퍼토리에 순서를 부여해 줍니다.
4) 문자코드세트(character codeset, character set)란?
레퍼토리와 대조열, 이 두 개념을 묶으면 문자코드세트의 개념이 완성됩니다. 때문에 문자에 할당된 코드값(정수값)까지를 통틀어서 문자세트라고 볼 수 있습니다. 여담이지만 문자세트라고 하면“문자들만”을 의미하는 것인지, 문자들을 포함하여 각 문자에 할당된“코드값 까지도”의미하는 것인지 모호 했거든요..
5) C언어의 기반이 되는 문자세트로 ASCII가 될 수 없는 이유는?
C언어는 다른 프로그래밍 언어에 비해 매우 다양한‘그래픽 문자(제어문자 이외의 문자)’를 사용합니다. 이는 기본적으로 C언어가 태어난 환경이 다양한 그래픽 문자를 지원하는 ASCII를 사용하던 시스템이기 때문입니다. 하지만 국제 표준을 갖게 될 C언어의 기반이 되는 문자세트를 ASCII로 할 수는 없었습니다. 왜냐하면 ASCII를 기반으로 한 ISO 646 IRV(International Reference Version)에서는 ASCII 문자세트 중 자주 사용되지 않는 그래픽 문자 12개(#, $, @, [, \, ], ^, `, {, |, }, ~)에 대해서는 필요에 따라 각 국가별로 자신들만의 문자를 할당할 수 있도록 국가별 ASCII 변종을 정의하여 사용할 수 있게 하였는데, 이러한 이유 탓에 국제 표준을 갖게 될 C언어를 ASCII에 의존시키는 것이 바람직하지 않았습니다.
6) C언어의 기반이 되는 문자세트는?
그래서 ASCII 대신 C언어를 기술할 수 있는 문자세트로 선정된 것이 ISO 646 Invariant Set입니다. 이는 ISO 646 IRV가 정의하는 여러 ASCII의 변종들 중 변화가 없는 부분만 모아놓은 문자세트를 말합니다. 즉 ASCII에서 자주 사용되지 않는 그래픽 문자 12개를 제외한 것이 Invariant Set이라 할 수 있습니다. 때문에 Invariant Set은 당연히 ISO 646 IRV 즉 ASCII의 부분집합입니다.
7) Trigraph Sequence가 필요한 이유는?
C언어가 기반으로 하고 있는 문자세트가 ISO 646 Invariant Set이므로, C언어를 지원하는 환경이라면 Invariant Set을 기반으로한 문자세트를 사용할 수 있지만, 정의되지 않은 그래픽 문자 9개(ISO 646 Invariant Set은 C언어에서 사용되는 모든 문자를 담고 있지 않습니다)는 다른 표현방법이 필요한데 이를 위해서 C표준에는 Trigraph Sequence를 도입하게 됩니다.
8) C언어를 지원하는 환경이 ASCII를 사용해도 되는 이유는?
“C언어의 기반이 되는 문자세트”라는 표현에 대해서 절대로 잊어서는 안 되는 중요한 것이 있는데, C표준이 ISO 646 Invariant Set을 기반 문자세트로 두었다는 이야기는 C가 사용하는 문자들만을 그 문자세트에서 빌려왔다는 것입니다. 따라서 C언어를 지원해주는 모든 환경이 대조열까지 동일한 문자세트를 사용해야 한다는 것을 의미하지는 않습니다. 다만 어떤 문자가 어떤 코드값을 갖는지 상관없이 최소한 ISO 646 Invariant Set에 있는 문자들을 모두 가지고 있는 어떠한 문자세트도 C언어 표준을 만족하는 환경이 될 수 있다는 것을 의미합니다.
9) ASCII는 256개의 문자이다?
ASCII는 기본적으로 정보 교환을 위해 1963년에 개발된 미국 표준 문자세트입니다. ASCII는 7비트 코드를 사용하므로 (즉 7자리의 이진 숫자로 구성된 코드값을 갖습니다)2^7=128개의 다른 문자를 표현할 수 있습니다. 0번부터 127번에 해당하는 128개의 문자 중 94개(33번~126번)가 화면에 출력되어 문자를 보여줄 의도로 정의된 인쇄문자이며, 32번(space)과 127번(delete)을 제외한 나머지 32개(0번~31번)는 다양한 용도로 사용되는 제어문자입니다. 하지만 안타깝게도 ASCII를 2^8=256개의 문자를 갖는 8비트 코드값을 사용하는 문자세트로 오해하고 있는 사람들이 있습니다. 이는 잘못된 것입니다. 현재의 많은 컴퓨터들이 문자 하나를 표현하기 위해 8비트의 공간을 할당하고 있는 것은 사실이지만, 표준으로서 ASCII는 여전히 7비트 코드를 사용하며, 남은 1비트 공간에 의해 128번부터 255번까지의 128개의 공간이 비어 있습니다(쓰이지 않습니다). 이렇게 주인 없이 비어있는 공간에 각 국가나 컴퓨터 산업체들이 자신들의 필요한 여러 문자를 배치하여 사용하기 시작했고, 이런 관행 때문에 언젠가부터 ASCII가 0번부터 255번까지의 코드값을 갖는 것으로 알려졌습니다. 사실 이렇게 ASCII를 확장하여 사용하는 행위가 잘못된 것은 아닙니다. 필요에 의해 확장된 ASCII를 비공식적으로는‘ASCII-8’혹은‘확장 ASCII(Extended ASCII)’라고 합니다. 중요한 것은 ASCII-8을 사용하는 시스템이라고 해도 표준인 ASCII가 정의해주는 것은 127번까지 이므로 128번 이후부터는 같은 코드값에 대해 각각 다른 종류의 문자가 정의될 수 있습니다. 따라서 일부 잘못된 ASCII에 대한 소개만 믿고, 그 이후 문자에 대해서도 동일한 의미가 보장될 것이라 생각한다면 큰 실수입니다(코드값 128번 이후의 문자가 서로 다를 수 있는 확장 ASCII의 예로 IBM-PC ASCII와 MS 윈도우 ASCII의 차이를 확인해 보시면 됩니다).
10) 크로스 컴파일러가 필요한(번역환경과 실행환경을 구분하는) 이유는?
void 임의로 가정한 상황(void)
{
새로 개발된 시스템이 도착했다. CPU를 포함한 모든 부분이 새로 개발된 것이기에 기존의 어떤 운영체제와도 호환되지 않습니다. 여러분에게 주어진 것은 오직 시스템 하드웨어에 대한 자세한 설명서와 원시적인 그 시스템만의 운영체제 그리고 장착된 CPU에서 사용할 수 있는 어셈블리어와 어셈블러 뿐이다. 여러분이 맡은 일은 많은 응용 프로그램을 개발하기 위해 그 시스템에서 작동하는 C컴파일러를 만드는 것이다. 어떻게 할 것인가? 물론 여러분은 새로운 어셈블리어를 쉽게 익힐 수 있으며 어셈블리 언어나 C언어로 C컴파일러를 만들 수 있다고 가정한다.
}
가장 고되지만 단순하게 생각할 수 있는 방법은 그 시스템에서 프로그램을 개발할 수 있는 유일한 도구는 시스템과 함께 제공된 어셈블리어 뿐이므로 빠른 시간내에 그 어셈블리어를 익혀서 C컴파일러를 만들면 됩니다. 또는 어셈블리어로 가장 단순한 형태의 C컴파일러를 만들고(단순한 형태의 초라한 C언어도 만들고) 초라한 C언어를 사용해 좀 더 복잡한 기능을 지원하는 또 다른 C컴파일러를 만드는 과정(좀 더 복잡한 기능을 지원하는 C언어도 만드는 과정)을 반복하여 최종적으로 원하는 수준의 작품을 얻어낼 수 있을 것입니다. 하지만 좀 더 현명한 방법은 익숙한 개발환경을 제공하는 시스템에서 새로운 시스템을 위한 C컴파일러를 만드는 것입니다. 새로운 C컴파일러를 기존의 환경에서 만들되, 새로 제작되는 C컴파일러가 출력하는 결과를 기존 환경이 아닌 새 환경에서 사용할 수 있는 새 시스템의 어셈블리어로 나오게 하면 됩니다. 혹은 이미 프로그램 소스를 가지고 있는 C컴파일러가 있다면 그 컴파일러의 결과 출력 부분만을 새 시스템의 어셈블리어로 바꾸면 되므로 문제가 더 간단히 해결될 수도 있습니다. 이렇게 제작된 새 컴파일러는 컴파일 작업 자체는 기존 환경에서 하지만, 컴파일의 결과물은 다른 시스템에서 실행될 수 있는‘크로스 컴파일러’가 되는 것입니다. 또한 크로스 컴파일러에 의해 수행되는 컴파일 작업을‘크로스 컴파일’이라고 합니다. 크로스 컴파일과 같이 프로그램을 컴파일하여 결과를 얻어내는 개발환경과 실제 그 프로그램을 사용하는 사용환경이 별개로 존재하는데, 이를 각각‘번역환경’과‘실행환경’으로 구분합니다. 즉 번역과 실행을 하나의 환경에서 한다는 것은 번역환경과 실행환경이 하나로 일치되는 경우일 때만 가능하며 C언어는 크로스 컴파일을 위해서라도(C표준은 크로스 컴파일을 배려합니다)번역환경과 실행환경을 구분해야 합니다.
11) 소스 문자세트와 실행 문자세트가 달라질 수 있는 이유는?
C언어는 크로스 컴파일을 배려하는 언어로 프로그램이 번역되는 번역환경과 번역된 프로그램이 실행되는 실행환경에 구분을 둔다고 이야기 했습니다. 물론 이 두 개의 환경은 C언어가 요구하는 번역과 실행을 위한 최소한의 요구를 만족한다는 점을 제외하면 더 이상의 공통점을 가질 필요가 없습니다. 이는 각 환경이 사용하고 있는 문자세트에도 그대로 적용됩니다. 따라서 각 환경에서 사용하는 문자세트도(같으면 상관이 없겠지만 다를 경우를 위해) 별도로 이름을 붙여 구분할 필요가 있습니다. 이에 번역환경에서 프로그램 소스를 작성하는데 쓰이는 문자세트를‘소스 문자세트’, 실행환경에서 프로그램이 실행 중에 입출력에 사용하는 문자세트를‘실행 문자세트’라고 합니다. 물론, 번역환경과 실행환경이 동일한 대부분의 경우에는 소스 문자세트와 실행 문자세트 역시 동일할 가능성이 큽니다(항상 동일하다는 보장은 없습니다).
12) 소스 문자세트와 실행 문자세트에 대한 C언어의 최소한의 요구사항은?
다음은 별개의 문자세트일 수 있는 이 두 개의 문자세트에 적용되는 표준의 요구사항입니다.
첫째, 두 문자세트가 동일하게 가지고 있어야 하는 문자들
- 알파벳 대소문자 52개
- 10개의 십진 숫자를 표시하는 문자
- 29개의 그래픽 문자
- 공백(공백과 공백문자는 서로 다른 개념)
- 수평탭
- 수직탭
- 폼피드
둘째, 소스 문자세트가 가지고 있어야 하는 문자들
- 한 줄의 끝을 표시하는 문자(End-Of-Line). 사실상 셋째에서의 개행문자와 동일하다고 생각할 수 있습니다.
셋째, 실행 문자세트가 가지고 있어야 하는 문자들
- 경보를 표시하는 제어문자
- 백스페이스
- 캐리지 리턴
- 개행문자
- 널문자
다음은 기억해야 할 몇 가지 주의사항입니다.
첫째, 십진 숫자를 표현하는 문자(0부터 9)는 순차적으로 증가하는 코드값을 가져야 합니다.
- 어떤 문자세트에서 0이라는 문자가 코드값 48을 갖는다면 이 문자세트에서 1부터 9까지의 문자가 각각 49부터 57까지의 코드값을 가져야만 표준이 요구하는 바를 만족시킨다는 뜻입니다.
둘째, 표준은 위에서 언급한 문자들의 모양까지 요구하지는 않습니다.
- 한글이 지원되는 대부분의 시스템에서 키보드를 통해 입력된 \(역슬래시) 문자는 원화로 나타나며, 일본 시스템에서는 엔화로 나타납니다. 중요한 것은 그것들이 모양에 상관없이 일관되게 \(역슬래시)를 의미하며, 다른 문자들과 구분된다는 점입니다.
셋째, 위에서 언급한 문자들은 표준이 요구하는 최소한의 문자들이지 이 이상의 문자들을 사용하지 말라는 뜻은 아닙니다.
- ASCII는 C언어가 요구하는 모든 문자들을 모두 포함하고도 추가로 여러 개의 제어문자와 그래픽 문자를 가지고 있습니다.
넷째, 십진 숫자를 표시하는 문자를 제외하고 다른 문자들의 순서는 따로 보장될 필요가 없습니다.
- ASCII의 폐해가 가장 크게 드러나는 부분입니다. ASCII에서는 영문 대문자와 영문 소문자가 순서대로 연속 배치되어 있습니다. 그 덕분에(?) 많은 사람들이 모든 환경에서도 마찬가지일 것이라 가정합니다. 그러나 이는 틀린 가정입니다. IBM 컴퓨터에서 종종 사용되는 EBCDIC 문자세트는 영문 대소문자가 연속적으로 나오지 않습니다(I와 J, R과 S).
13) C 표준에서 말하는 바이트와 문자란?
바이트는 실행환경 문자세트의 문자 중 임의의 한 문자를 저장할 수 있을만큼의 크기(즉 비트 개수)를 갖는 메모리의 단위입니다(번역환경이 아닌 실행환경 문자세트라고 하고 있습니다).
문자는 한 바이트의 크기(즉 바이트의 정의에 의해 요구되는 비트 개수)에 맞는 임의의 비트열입니다.
참고로 C언어에서 사용하는“문자”라는 용어에는 두 가지 개념이 중첩되어 있습니다. 하나는 우리가 직관적으로 이해하고 있는 일상적인 문자의 개념이고, 다른 하나는 위에서 정의하고 있는 문자의 개념입니다. 따라서 C언어에서 사용되는“문자”라는 용어는 문맥에 따라 이 두 개념 중 하나를 선택하여 이해해야 합니다.
14) 바이트에 대한 추가적인 제한은?
하나는 바이트의 크기가“최소한”8비트 이상이어야 하며, 다른 하나는 각 바이트는 메모리에서 고유한 주소값을 가져야 한다는 것입니다.
- 우선 바이트의 크기는 실행환경 문자세트가 얼마나 많은 문자를 가지고 있느냐에 따라 그 최종적인 크기가 결정됩니다. 많은 사람들은 바이트를 8비트를 묶어 부르는 용어로 이해하고 있습니다. 이는 대부분의 사람이 잘못 알고 있는 지식입니다. 절대 바이트는 8비트를 묶어 부르는 용어가 아닙니다(정확히 8비트를 부르는 용어로‘옥텟’이 있습니다). 다만 해당 컴퓨터에서 한 문자를 저장하기에 충분할 만큼의 공간을 이야기하는 것으로, 요즘 사용하는 대부분의 컴퓨터가 우연히 8비트를 사용하고 있을 뿐입니다. 실제 비록 소수이지만, 1바이트를 9비트나 16비트로 정의하는 컴퓨터도 있습니다. 따라서 만약 실행 문자세트의 총 문자 개수가 512개라면 당연히 한 바이트의 크기는“최소”9비트가 될 것입니다.
- C에서 한 바이트의 크기는 최소 8비트가 되어야 합니다. 즉 한 바이트로 7비트를 사용하는 컴퓨터들은 C언어를 제대로 지원하지 못하거나 혹은 (지원하기 위해서)편법을 사용해 C프로그램의 입장에서는 1바이트가 8비트 이상인 것처럼 보이도록 만들어야 합니다.
- 각각의 바이트는 메모리에서 별도의 주소값으로 접근될 수 있어야 합니다. 즉 컴퓨터가 메모리의 위치를 가리키는 어떤 주소값을 사용하여 메모리에 접근할 때 가장 최소가 되는 단위가 바이트여야 합니다. 물론 대부분의 컴퓨터가 바이트 기반의 주소 체계를 사용하는 요즘에는 이러한 사실이 당연한 것으로 생각될 수 있지만, (바이트보다 일반적으로 크기가 큰)워드 단위로의 접근만 허락하는 워드 기반의 주소 체계를 사용하는 일부 컴퓨터에서는 골치 아픈 편법을 사용해야 C언어를 올바르게 지원할 수 있음을 의미합니다.
- 이제 C언어에서 기억장치를 다루는 가장 기본적인 단위는 바이트가 됩니다. C에서 다루는 바이트보다 큰 모든 단위는 바이트의 정수배 크기를 갖는 것으로 정의됩니다.
15) 문자에 대한 추가적인 주의사항은?
간단히 한 바이트에 저장할 수 있는 비트열을 문자라고 합니다. 즉 한 바이트가 9비트로 구성되어 있다면 코드값 0을 갖는 문자부터 코드값 511을 갖는 문자까지 총 512개의 문자가 있습니다. 한 바이트에 저장할 수 있는 수치값은 모두 어떤 문자의(문자세트에 해당 코드값을 갖는 문자가 실제로 존재하든 아니든) 코드값이 되고, 우리는 그것을“문자”라고 합니다.
- C언어를 지원하는 모든 환경이 반드시 포함하고 있어야 하는 문자들은 반드시 한 바이트에 들어갈 수 있는 크기의 코드값을 가져야 합니다(실행 문자세트 뿐만 아니라 번역 문자세트까지도). 즉 만약 바이트가 8비트인 경우, C언어가 요구하는 문자 중 하나인 #가 2500이라는 코드값을 가져서는 안됩니다.
16) 코드 페이지(code page)란 무엇인가요?
1바이트가 8비트인 시스템에서 ASCII 코드를 사용하면 1비트가 남습니다. 다시 말해 ASCII 코드는 0000 0000(0) ~ 0111 1111(127) 까지만 쓰기 때문에 1000 0000(128) ~ 1111 1111(255)까지의 영역이 남는다는 뜻입니다. 이렇게 주인없이 방치된 영역에 ASCII 코드에 포함되지 않는 문자를 사용하는 국가들이 자국의 문자를 채워넣기 시작했습니다. 문제는 이런 과정이 통제된 방식이 아닌 각 국가들의 독자적인 방법으로 동시에 진행됐다는 점입니다. 0~127까지의 영역은 이미 표준으로 자리잡은 ASCII 코드 방식으로 해석을 하면 문제가 없었지만, 128이상의 영역은 각 국가마다 할당된 문자들이 달랐기 때문에 이 영역의 수를 문자로 바꾸기 위해서는 또 다른 방법이 필요했습니다. 이 해석 방법이“코드 페이지”입니다. 어떤 8비트 코드(숫자)가 주어졌을 때 현재 코드 페이지가 737(그리스)라면 127이하의 문자는 기존의 ASCII 방식으로 해석하고, 128이상의 문자는 그리스 문자를 위한 방식으로 해석해야 합니다.
17) 코드 페이지에서의 바이트/문자 충돌(conflict)이란?
중국, 일본, 한국과 같이(이를 통틀어서 CJK라고 부릅니다) 많은 문자를 사용하는 국가에서는 바이트(8비트) 하나로는 이 많은 문자를 표현할 방법이 없습니다. 따라서 더 이상“바이트=문자”라는 가정은 참이될 수 없는데, 이를 가리켜“바이트/문자 충돌”이라고 합니다.
18) 바이트/문자 충돌 문제를 해결하기 위한 방법은?
바이트보다 큰 단위의 기억단위를 도입한다면 이 문제를 해결할 수 있습니다. 그리고 이를 가리켜 여러 개의 바이트를 사용하여 문자를 표현한다 해서 MBCS(Multi Byte Character Set)라고 합니다. MBCS 개념이 도입되면서 한 바이트로 충분히 표현할 수 있는 문자들을 구분할 필요가 있는데, 이를 가리켜 SBCS(Single Byte Character Set)라고 합니다. 중국, 일본, 한국 그리고 대만 등에서 자국의 문자를 표현하기 위해 2바이트 기억단위를 사용하는데 이를 DBCS(Double Byte Character Set)라고 부르기도 합니다.
19) MBCS는 SBCS를 포함하는 관계인가요?
한 바이트로도 충분히 표현할 수 있는 문자를 의미하는 SBCS은 여러 개의 바이트를 사용하여 문자를 표현하는 MBCS에 완전히 포함되는 관계입니다. 바이트/문자 충돌 문제를 해결하기 위한 MBCS에서 SBCS을 지원하기 위해 중국, 일본, 한국등의 코드 페이지 처음 127개는 ASCII와 일치하며, 첫 번째 바이트를(Leading Byte)라 하고 두 번째 바이트를(Trailing Byte)라 합니다. 예를 들어, 첫 번째 바이트의 최상위 비트가 0이라면 즉 127이하라면 ASCII 코드로 해석하면 되고, 최상위 비트가 1이라면 즉 128이상이라면 두 번째 바이트까지를 한글로 해석하면 됩니다.
20) 인코딩(encoding)이란?
멀티바이트 문자를 메모리 공간에 논리적으로 어떻게 표현할 것인지 정해놓은 약속을 의미한다고 생각합니다. 한국은 한글을 표현하기 위해 2바이트를 사용하는데, 이 메모리 공간에 어떻게 한글을 기록할 것인지에 대한 서식을 인코딩이라 볼 수 있을 것 같습니다(간단히 말하면 자료형이 존재하는 이유와 인코딩이 존재하는 이유는 동일합니다).
20-1) 완성형과 조합형
128~65535라는 영역이 한글을 위한 공간으로 주어졌지만 이곳에 한글을 어떻게 할당하느냐는 문제(인코딩)를 결정해야 합니다. 여기에 대해서 두 가지 의견이 존재했습니다. 첫 번째는 한글 하나 하나를 독립된 개념으로 생각하고 각 문자에 코드를 부여하는 의견입니다. 예를들면“가”는 128,“나”는 129,“다”는 130을 할당하는 방식입니다. 두 번째는 한글의 창제 원리인 초성, 중성, 종성의 개념을 살려서 한글이 사용할 수 있는 15비트(최상위 1비트는 한글임을 표시하기 위한 비트)를 3등분 해서 초성, 중성, 종성에게 각각 5비트를 할당하자는 의견입니다. 즉 1xxxxxyyyyyzzzzz라는 비트 구조가 됩니다. 예를들어 초성“ㄱ”이 1, 중성“ㅓ”가 3, 종성“ㄱ”이 1이라면“걱”은 1 00001 00011 00001으로 표현됩니다. 전자를 한글 하나 하나를“완성”된 문자로 본다는 의미에서 완성형 인코딩이라 하고, 후자를 한글 하나는 초성, 중성, 종성이“조합”된 결과라는 의미에서 조합형 인코딩이라 합니다.
20-2) EUC-KR
안타깝지만 조합형은 표준으로 채택되지 않았고, 완성형 인코딩 방식의 다른 이름이 EUC-KR입니다. EUC-KR은 ks x 1003과 ks x 1001으로 구성됩니다. ks x 1003은 역슬래시 대신에 원화 표시를 사용한다는 점을 제외하면 ASCII 코드의 문자들과 같습니다. ks x 1001은 한글과 특수문자, 한자등을 포함합니다. EUC-KR은 127이하에는 ks x 1003을, 128 이상에는 ks x 1001을 할당 했습니다. 128 이상이라고 하지만 실제로 EUC-KR이 사용한 공간은 상위 바이트 161~254, 하위 바이트 161~254뿐입니다. 더군다나 ks x 1001에는 한글 외에도 특수문자와 한자 등의 문자가 많기 때문에 그렇지 않아도 작은 이 영역에 들어가지 못한 한글이 생겼습니다. 바로“똠”이나“뷁”등입니다. 간혹 인터넷 게시물의 문자가 보이지 않았던 경험이 있을 것입니다. 그 문자들이 바로 ks x 1001, 그리고 EUC-KR에서 제외된 글자들입니다.
20-3) CP949
자주 사용되지 않는 몇몇 한글을(똠, 뷁등) EUC-KR에서 사용할 수 없다는 점이 어떤 사람들에게는 별 문제가 아니었지만 다른 어떤 사람들에게는 큰 문제 였습니다. 이때 등장한 것이 마이크로소프트의 CP949 즉 코드 페이지 949입니다. CP949는 마이크로소프트가 ks x 1001에 없는 한글 8822개의 문자를 추가해서 EUC-KR을 확장한 완성형 인코딩 방식입니다. 128 이상의 영역 중 EUC-KR이 사용하지 않던 영역에 8822개 문자를 할당했습니다. 그래서 CP949를 확장 완성형 또는 통합형 한글 코드라고도 불립니다. 마이크로소프트에서는 cp949를 ks_c_5601_1987이라고도 부릅니다.
21) 유니코드(unicode)가 만들어진 이유는?
유니코드란 지구상의 모든 언어의 문자를 표현하기 위해 만들어진 거대한 문자세트입니다. 이러한 유니코드가 필요했던 이유는, 전세계에는 다양한 국가와 민족이 살고 있고 수 많은 언어와 문자가 존재합니다. 이렇게 수 많은 언어의 문자를 컴퓨터상에서 표현할 때 서로 다른 문자세트와 인코딩 방식을 사용하게 됩니다. 이로 인해 하나의 멀티바이트 코드를 문자로 해석하기 위한 인코딩은 여러 개가 존재하게 되며, 자연스레 멀티바이트 코드에 대응하는 문자 역시 오직 하나가 될 수 없다는 문제가 발생합니다. 이와 같은 문제에서 발생할 수 있는 비효율적 혹은 불편함 때문에 미국의 많은 소프트웨어 산업체들이 유니코드의 개념을 필요로 하지 않았나 생각하는 바입니다. 즉 유니코드를 사용하면 전 세계의 문자 각각에 고유한 코드가 부여되어 겹치는 일이 없습니다. 여러 언어에서 공통적으로 쓰는 문자는 별다른 어원상의 문제가 없다면 일반적으로 같은 코드를 가질 순 있겠지만, 어쨌거나 중요한 것은 문자 하나가 코드 하나에 고유하게 대응된다는 사실입니다.
22) ISO 10646이란?
사실 지구상의 모든 언어의 문자를 표현하기 위한 문자세트는 유니코드가 아닌 ISO(국제표준기구)에 의해 먼저 진행되었는데 ISO 10646이란 국제 표준이 그것입니다. 이 국제 표준은 4바이트 체계로서 모든 문자에 4바이트를 할당하여 사용하자는 것이었습니다. 이를 가리켜 Universal Character Set-4, 줄여서 UCS-4라고 합니다(정확하게는 32비트가 아닌 31비트를 사용하기 때문에 약 21억 개의 문자를 수용할 수 있습니다). 이와 관련된 용어로는 Cell, Plane, Group가 있습니다.
Cell: 간단히 하나의 문자를 의미합니다.
Plane: 256x256(=65536)개 Cell의 묶음. Plane 00부터 Plane FF까지 존재합니다.
Group: 256개 Plane의 묶음. Group 00부터 Group 7F까지 존재합니다.
Group마다 256개의 Plane이 존재하며, 각 Plane은 65536개의 Cell로 구성됩니다. 따라서 65536x256x128하면 대략 21억 개라는 문자가 나옵니다. 그리고 최초 65536개의 문자가 할당되는 영역을 특별히 BMP(Basic Multilingual Plane: 기본언어판)라고 합니다. UCS-2는 모든 문자에 2바이트를 할당하므로 BMP 영역의 문자만 사용할 수 있습니다.
23) ISO 10646과 unicode의 관계는?
지지부진 하던 ISO 10646의 다국어 부분을 unicode에서 제정하는 표준으로 대체해 사용할 것을 국제 표준 기구에 제안하였고, 그 결과 ISO 10646 중에서 BMP 부분은 unicode와 동일하게 되었습니다. 즉 unicode는 ucs에 포함되는 부분집합 이라고 할 수 있습니다. ucs는 약 21억 개의 문자를 수용할 수 있는 반면 unicode는 Group 00의 처음 Plane 17개까지만 사용합니다. 즉 17x65536하면 약 110만 개의 문자를 수용할 수 있습니다. 하지만 두 규격 모두 실제 할당된 문자는 서로 완전히 일치합니다.
24) 유니코드는 정말 2바이트(16비트) 일까요?
많은 분들이 유니코드가 2바이트 체계라고 알고 계시지만, 실제 유니코드는 21비트 체계라고 할 수 있습니다. 유니코드가 수용하는 약 110만 개의 문자를 사용하려면 16비트로는 턱 없이 부족합니다. 다른 Plane의 문자까지 사용하려면 (총 17개의 Plane이므로)5비트를 추가로 가져야 합니다. 그래서 21비트 체계여야 합니다.
25) 유니코드에서의 3가지 인코딩이란?
UTF-32는 유니코드가 수용하는 약 110만 개의 문자 모두를 단순히 32비트로 만드는 것입니다.
UTF-16은 BMP 범위 내의 문자라면 16비트로 만들지만, BMP 범위를 벗어나는 문자는“특별한”변환을 거쳐 32비트로 만듭니다. 무엇보다 중요한 것은 32비트라도 UTF-16과 UTF-32의 32비트는 값이 다릅니다.
UTF-8은 ASCII 문자마저도 2바이트가 된다는 UTF-16의 문제를 피하기 위해 제안되었습니다.“ABC”를 UTF-16으로 인코딩하면 00 41 00 42 00 43이 됩니다. 문자열 중간에 널(null)이 들어가버리므로 문자열 관련 C 함수들이 정상적으로 동작하지 않게 됩니다. UTF-8 인코딩은 ASCII 호환성을 주목적으로 하기 때문에 중간에 널(null, 0)이 들어가지 않도록 고안되었습니다. 즉 ASCII 문자는 1바이트가 되며, 나머지 문자는 코드값의 범위에 따라 2, 3, 4바이트로 서로 달리 인코딩됩니다.
26) C언어에서의 확장문자(wide character)란?
C에서 요구하는 기본적인 문자 외에 바이트보다 큰 단위를 사용해 다루는 문자를‘확장문자’라고 합니다. 또한 확장문자를 통해서만 다룰 수 있는 추가적인 문자들의 집합을‘확장 문자세트(extended character set)’라고 합니다. 따라서 번역환경과 실행환경을 구분하는 C언어는 다음과 같이 네 가지의 문자세트로 구분될 수 있습니다.
기본 소스문자세트(basic source character set)
기본 실행문자세트(basic execution character set)
확장 소스문자세트(extended source character set)
확장 실행문자세트(extended execution character set)
C언어가 반드시 존재하기를 요구하는 문자들이 기본 문자세트에 속합니다.
27) 입출력을 제외한 프로그램 내에서 확장문자를 자유롭게 다룰 수 있는 이유는?
표준이 단일바이트의 일반 문자들에 대해서만 제공하던 여러 가지 라이브러리를, 바이트보다 더 큰 기억단위를 사용하는 확장문자에도 사용할 수 있도록 동일한 기능의 라이브러리를 대폭 추가했기 때문입니다. 이를 통해 프로그램 내의 확장문자를 마치 단일바이트 문자를 다루듯이 자유롭게 다룰 수 있습니다.
28) 입출력에서 발생할 수 있는 확장문자의 문제점은?
아무리 확장문자를 단일 문자처럼 잘 다룰 수 있다고 해도 입출력이나 파일로 저장할 때는(사실 파일로 저장하는 것 역시 출력의 일종입니다) 바이트 단위여야 합니다(C언어의 기본문자는 바이트이고, 확장문자 역시 여러 바이트일 뿐이므로?). 따라서 외부와의 입출력을 위해 (내부적으로는 어디까지나 한 바이트처럼 하나의 기본단위로 다루어지는)확장문자를 반드시 바이트로 다룰 수 있어야 합니다. 물론 바이트보다 큰 단위로 다루어지는 문자(사실상, 코드값)를 단일바이트 안으로 구겨넣는 것은 말이 안됩니다. 그래서 적절한 약속과 조합(인코딩)을 결정해서 여러 개의 바이트가 확장문자 하나와 대응관계를 맺도록 만들어 이 문제를 해결하였습니다. 이때 여러 바이트가 한 문자를 상징한다는 뜻에서 확장문자에 대응하는 여러 개의 바이트 묶음을‘멀티바이트 문자(multibyte character)’라고 합니다.
29) 확장문자와 멀티바이트 문자의 차이점은?
둘 다 어떤 확장 문자세트의 한 문자를 의미하는데 사용되는 개념이지만 확장문자는 그 문자의 내부적인 표현으로 하나의 단위로 다루어져 표준이 제공하는 문자에 가해지는 여러 작업을 손쉽게 수행할 수 있도록 해주지만, 멀티바이트 문자는 한 문자를 여러 개의 바이트에 대응시켜 줌으로써 확장문자가 쉽게 입출력될 수 있도록 돕는다는 차이점이 있습니다.
30) strlen(“가”);의 반환값은?
strlen 함수는 문자(확장문자가 아닙니다)의 길이(개수)를 반환해주는 함수이므로 (분명“가”는 한 글자입니다)반환값으로 1이 나올 것 같지만, 실제로는 2가 반환됩니다. 이유는 간단합니다.“가”가 2바이트로 이루어진 멀티바이트 문자이고, 결국 내부적으로 2개의“문자”로 이루어진 배열이기 때문입니다. 따라서 2라는 값을 반환하는 strlen 함수의 행동은 매우 정당한 것입니다.
31) 확장문자가 필요한 이유는?
만약“가”라는 문자열을 진정 우리가 바라보는 하나의 글자를 갖는“가”로 보이게 하려면이를 확장문자로 다뤄야 합니다. 만약 이를 확장문자로 변환한 값이 16비트 정수값 6(00000000 00000110)이라고 가정하면, 이 확장문자로 구성된 문자열을 strlen 함수의 확장문자 버전인 wcslen 함수에 넘겨줄 경우 당연히 반환되는 값은 우리 상식에 부합되는 1이 될것입니다. 이것이 확장문자가 필요한 이유입니다.
32) 멀티바이트 문자에 가해지는 여러 가지 제한 중 하나는?
C에서는 문자열의 끝을 표시하기 위해 코드값이 0인 널문자를 사용하는데, 위에서와 같이 “가”라는 확장문자의 코드값이 6이므로 00000000 00000110에 strlen 함수를 적용한 결과는 2보다 훨씬 덜 직관적인 0이 나올것입니다. 이런 이유로 표준은 멀티바이트 문자가 갖추어야 하는 여러 가지 제한 중 하나가 문자를 구성하는 바이트들 중간에 널문자인 0이 있어서는 안된다고 제한하고 있습니다.
33) Visual Studio의 소스파일(.c) 인코딩은?
표준이 모든 임플리멘테이션에 대해 적절한 확장 문자세트를 지원하라고 요구하는 것은 아닙니다. 그러나 보니 운영체제 및 컴파일러에 따라 조금씩 차이가 있습니다. 다만 ASCII같은 단일바이트 문자만 문자세트로 제공하는 환경에서는 확장 문자세트가 기본 문자세트와(멀티바이트 문자가 단일바이트 문자와) 동일해지는 것뿐입니다. Visual Studio는 cp949이 기본 인코딩이며 UTF-8도 가능합니다. 명령 프롬프트의 경우 다음 명령어로 각각 영문 모드, 한글 모드, UTF-8의 인코딩으로 변경할 수 있습니다.
chcp 437, chcp 949, chcp 65001
참고로 cp949와 EUC-KR은 한글 1글자를 2바이트로 저장하는데, UTF-8은 한글 1글자를 3바이트로 저장합니다.