8. Characters and Strings

C has no built-in string type; by convention, a string is represented as an array of characters terminated with '\0'. Furthermore, C hardly has a character type; a character is represented by its integer value in the machine's character set. Because these representations are laid bare and are visible to C programs, programs have a tremendous amount of control over how characters and strings are manipulated. The downside is that to some extent, programs have to exert this control: The programmer must remember whether a small integer is being interpreted as a numeric value or as a character (see question [*]8.6) and must remember to maintain arrays (and allocated blocks of memory) containing strings correctly.

See also question [*]13.1 through [*]13.7, which cover library functions for string handling.



Q 8.1
왜 다음 코드는 실행이 안되는 것일까요?
  strcat(string, '!');
Answer
문자열과 문자는 큰 차이가 있습니다. strcat() 함수는 문자열을 이어주는 함수입니다.

'!'와 같은 문자 상수(character constant)는 한 문자를 나타냅니다. 문자열(string literal)은 쌍따옴표로 둘러싼, 대개 여러 글자의 문자로 구성됩니다. "!"와 같은 문자열은 하나의 문자를 나타내는 것처럼 보이지만, 실제로 ! 문자와, 문자열 끝을 나타내는 \0, 두 개의 문자로 구성됩니다.

C 언어에서 문자는 문자 집합(character set)에서 그 문자가 나타내는 작은 정수 값으로 표현됩니다 (질문 [*]8.6을 참고하기 바랍니다). 문자열은 문자들의 배열로서 표현됩니다; 문자열을 다룰 때에는 보통 문자 배열의 첫 요소를 가리키는 포인터를 써서 합니다. It is never correct to use one when the other is expected. 문자열에 !를 이어 붙이려면 다음과 같이 해야 합니다.

  strcat(string, "!");

질문 [*]1.32, [*]7.2, [*]16.6을 참고하기 바랍니다.

References
[CT&P] § 1.5 pp. 9-10



Q 8.2
문자열이 어떤 특정한 값과 같은지 검사하려고 합니다. 다음과 같이 코드를 만들었는데 왜 동작하지 않을까요?
  char *string;
  ...
  if (string == "value") {
    /* string matches "value" */
    ...
  }
Answer
C 언어는 문자열을 문자의 배열로 처리합니다. 그리고 C 언어에서는 배열 전체에 대해 어떤 연산을 (대입, 비교 등) 직접 할 수 있는 방법은 없습니다. 위의 코드에서 == 연산은 피연산자인 포인터의 값을 비교합니다 -- 즉 변수 string의 포인터 값과 문자열 "value"를 가리키는 포인터 값을 비교합니다 -- 따라서 두 개의 포인터가 같은 곳을 가리키는지를 비교합니다. 대개의 경우 이 값이 같게 될 경우는 거의 없으므로, 이 비교는 거의 항상 같지 않다고 나옵니다.

두 문자열을 비교하는 방법으로 라이브러리 함수인 strcmp()를 쓰는 것이 가장 좋습니다:

  if (strcmp(string, "value") == 0) {
    /* string matches "value" */
    ...
  }



Q 8.3
다음과 같이 할 수 있다면:
  char a[] = "Hello, world!";
왜 이렇게는 할 수 없을까요?
  char a[14];
  a = "Hello, world!";
Answer
문자열은 배열입니다. 그리고 배열에는 직접 대입 연산을 쓸 수 없습니다. strcpy() 함수를 쓰기 바랍니다:
  strcpy(a, "Hello, world!");
질문 [*]1.32, [*]4.2, [*]6.5, [*]7.2를 참고하기 바랍니다.



Q 8.4
strcat이 동작하지 않을까요? 아래 코드처럼 했는데, 잘 안됩니다:
  char *s1 = "Hello, ";
  char *s2 = "world!";
  char *s3 = strcat(s1, s2);
Answer
질문 [*]7.2를 보기 바랍니다.



Q 8.5
아래 두 초기화 문장의 차이가 있나요?
  char a[] = "string literal";
  char *p = "string literal";
제 프로그램에서 p[i]에 어떤 값을 대입하려 하면 프로그램이 비정상적으로 끝나버립니다.
Answer
질문 [*]1.32를 보기 바랍니다.



Q 8.6
문자에 해당하는 수치 (ASCII 또는 다른 문자 set code)) 값을 어떻게 얻을 수 있죠? 또 그 반대로 수치 값에서 그 값에 해당하는 문자를 어떻게 얻을 수 있죠?

Answer
C 언어에서 문자는 (컴퓨터의 문자 셋의) 문자 코드 번호로 작은 정수로서 표현됩니다. 따라서 질문한 것과 같은 변환이 필요없습니다; 즉 문자를 가지고 있다면, 그 자체가 값이 됩니다. 예를 들어, 다음 코드는:
  int c1 = 'A', c2 = 65;
  printf("%c %d %c %d\n", c1, c1, c2, c2);
ASCII를 쓰는 시스템에서, 다음과 같은 출력을 만들어 냅니다:
  A 65 A 65
덧붙여 질문 [*]8.9, [*]20.10도 참고하시기 바랍니다.



Q 8.7
C 언어에서, 다른 언어에서 제공하는 것처럼, 문자열의 일부를 뽑아내는, “substr”와 같은 기능이 있나요?
Answer
질문 [*]13.3를 보기 바랍니다.



Q 8.8
사용자가 입력한 문자열을 읽어서 배열에 저장한 다음, 나중에 출력하려고 합니다. 사용자가 \n와 같은 문자를 입력한 경우, 왜 제대로 처리되지 않을까요?
Answer
\n와 같은 문자 시퀀스(character sequence)들은 컴파일할 때 해석됩니다. 문자 상수나 문자열에 백슬래시가 나오고 바로 뒤에 n이 나오면, 한 글자, newline 문자로 해석됩니다. (다른 character escape sequence도 비슷한 방법으로 처리됩니다.) 사용자나 파일에서 문자열을 읽을 때는 이와 같은 해석이 적용되지 않습니다: 즉, 백슬래시는 다른 문자와 전혀 다를게 없이 취급되며, 하나의 문자로 간주됩니다. (run-time I/O가 일어날 때, newline 문자를 위해 어떤 번역 작업이 이뤄질 수 있지만, 이 것은 전혀 다른 문제입니다. 질문 [*]12.40을 보기 바랍니다.) 덧붙여 질문 [*]12.6도 참고하시기 바랍니다.



Q 8.9
제 컴파일러에 버그가 있습니다. sizeof('a')의 값이 sizeof(char)인 1로 나오지 않고, 2가 나옵니다.
Answer
놀랍게도, C 언어에서 문자 상수(character constant)의 타입은 int입니다. 따라서 sizeof('a')sizeof(int)와 같습니다. (C++에서는 조금 다릅니다.) 덧붙여 질문 [*]7.8도 참고하시기 바랍니다.
Note
참고로 C++에서 문자 상수의 타입은 char입니다. 즉, sizeof('a')sizeof(char)와 같습니다.
References
[ANSI] § 3.1.3.4
[C89] § 6.1.3.4
[H&S] § 2.7.3 p. 29



Q 8.10
I'm starting to think about multinational character sets. Should I worry about the implication of making sizeof(char) be 2 so that 16-bit character sets can be represented?
Answer
만약 char가 16 bit가 된다고 해도, sizeof(char)는 여전히 1이 됩니다. 대신 <limits.h>에 정의된, CHAR_BIT이 16이 됩니다. 이 경우에는 한, 8-bit 오브젝트를 선언하는 것은 (또, malloc으로 할당하는 것도) 불가능합니다.

전통적으로, 한 바이트가 꼭 8 bit일 필요는 없습니다. 단지, 한 글자를 저장하기에 충분한, 작은 크기의 메모리 공간이면 됩니다. C 표준에서도 이 방식을 따르며, 따라서 malloc이나 sizeof에서 쓰이는 바이트가 8 bit 이상일 수도 있습니다.8.1 (대신 표준에서, 바이트가 8 bit 이상되어야 한다고 정해 놓았습니다.)

Multinational character set을 처리하기 위해서는 char 이상의 어떤 타입이 필요하고, ANSI/ISO C 표준에서는 이를 위해, 더 큰 범위를 포함할 수 있다는 뜻의 “wide” 문자 타입인, wchar_t를 제공합니다. 또한 이 타입으로 처리하는 wide 문자 상수, wide 문자열, 또 wide 문자 및 문자열을 처리할 수 있는 함수들을 제공합니다.

덧붙여 질문 [*]7.8도 참고하시기 바랍니다.

References
[ANSI] § 2.2.1.2, § 3.1.3.4, § 3.1.4, § 4.1.5, § 4.10.7, § 4.10.8
[C89] § 5.2.1.2, § 6.1.3.4, § 6.1.4, § 7.1.6, § 7.10.7, § 7.10.8
[ANSI Rationale] § 2.2.1.2
[H&S] § 2.7.3 pp. 29-30, § 2.7.4 p. 33, § 11.1 p. 293, §§ 11.7, 11.8 pp. 303-10

Seong-Kook Shin
2018-05-28