작은숲:위키노트/초보자를 위한 C 언어 강의/문자열
C 언어에서는 문자열의 표현할 수 있는 데이터 형이 없다. 그렇다면 C 언어에서는 문자열을 어떻게 사용할까? 먼저 C 언어에서 문자열을 표현하는 방법에 대해 알아보도록 하자. 문자열이라는 것은 결국 문자들의 집합이다. 하지만, 일반적인 문자들의 집합, 즉 char 형 배열과 다른 점은 문자열은 끝에 항상 '널 문자'('\0')가 있다는 것이다. 이 널 문자를 확인하면 이것이 문자열이라는 것을 판별할 수 있다. 따라서, 끝에 널문자가 없는 문자열이란 존재할 수 없으며, 문자열에서 가장 중요한 것은 바로 이 널 문자이다. 아래 예를 보자.
char str1[] = { 'H', 'e', 'l', 'l', 'o' };
char str2[] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char str3[] = "Hello";위에는 세 개의 배열이 있다. 이 세 개의 배열이 모두 문자열일까? 아니다. 제일 위의 배열은 문자들의 집합 끝에 널 문자가 없다. 그래서, 문자열이 될 수 없다. 하지만, 두번째 배열은 마지막에 널 문자가 존재함으로 문자열이다. 세번째 배열은 문자열 상수를 배열에 저장하는 것이다. 이 문자열 상수는 문자들의 집합이다. 그리고 시작과 끝에 쌍따옴표를 찍음으로서 이것이 문자열이라는 것을 나타내고 있다. 문자 상수는 따옴표만 찍는다. 이 문자열 상수는 단순히 문자들의 집합은 아니다. 위에서 말했던 대로 문자들의 집합 끝에 널 문자가 존재한다. 지금 우리에게는 보이지 않지만, 문자 집합 뒤에는 널 문자가 존재한다. 이 문자열 상수가 메모리에 저장될 때는 이렇게 마지막에 널 문자가 붙게 된다. 여기에서 주의해야 할 것은 문자열 상수이다. 문자열 상수는 이 문자열이 저장되어 있는 메모리의 시작 주소를 나타내는 포인터이다. 즉, 변수와 마찬가지로 상수라는 것도 메모리 어딘가에는 저장이 되어야 한다. 저장이 되어야 그 상수를 쓸 수 있는 것이다. 이러한 의미에서 본다면 배열과 비슷한 점이 있다. 배열의 이름이 배열의 시작 주소를 가리키는 포인터라는 건 알고 있을 것이다. 마찬가지로 문자열도 이 문자들의 집합이 저장되어 있는 곳의 시작 주소를 가리키는 포인터이다. 문자열을 변수에 저장하기 위해서는 char 형 배열이나 포인터를 이용한다. 그런데 이 두가지 방법이 조금 다르다. 그 이유는 배열과 포인터의 차이 때문에 일어난다. 우선 포인터라는 것은 '주소'를 저장하는 변수이다. 배열은 일정한 저장 공간(배열의 크기)의 '시작 주소'를 가리키고 있는 포인터이다. 배열은 배열 자체에 어떤 값들을 저장시킬 공간이 있다. 하지만, 포인터는 그 자체로는 어떤 값을 저장시킬 수 없다. 포인터는 어떤 값이 저장되어 있는 곳을 가리키는 일만 할 뿐이다. 따라서 포인터에 문자열을 저장한다는 것은 그 문자열이 저장된 공간의 시작 주소를 저장한다는 것이다. 반면에 배열의 경우는 배열에 어떤 값을 저장할 수 있기 때문에 그 공간에 문자열을 저장시킨다. 반드시 마지막에는 널문자가 추가되어야 한다. 그렇지 않다면 단순히 문자형 배열이 된다.
char *ptr = "Hello";
char str[] = "Hello";이렇게 문자열을 저장하기 위해 두가지 방법을 사용한다. 포인터에는 문자열 상수가 저장된 공간의 시작 주소를 저장시키고, 배열에는 배열의 저장 공간에 문자열을 '복사'하게 된다. 그렇다면 아래의 경우는 어떻게 될까?
char str[81];
str = "Hello";위의 경우는 우선 배열을 선언해두고, 나중에 그 배열에 문자열을 저장하고자 하는 것이다. 하지만, 저 코드에는 문제점이 있다. 어떤 문제일까? 문제는 배열에 있다. 만약 str이 그냥 포인터라면 아무런 문제도 없을 것이다. 하지만, str은 배열이다. 배열의 이름은 배열의 시작 주소를 가리키는 포인터라고 했다. 그리고 배열의 시작 주소는 바뀔 수가 없다. 위에서 마지막 연산의 의미는 '현재 str 배열의 시작 주소를 문자열 상수의 시작 주소로 대입해라'이다. 이게 가능할까? 불가능하다. 배열의 시작 주소는 절대 불변이다. 즉, 바꿀 수 없다는 말이다. 따라서, 위와 같은 코드는 사용할 수 없다. 하지만, 아까는 배열에 바로 문자열을 저장하지 않았나? 이것은 배열의 선언과 초기화를 같이 할 때만 가능하다. 즉, 아래와 같은 경우만 가능한 것이다.
char str[81] = "Hello";이런 경우는 자동으로 문자열 상수의 각 문자들을 배열에 저장한다. 즉, 아래와 같은 연산이 내부적으로 일어난다고 할 수 있다.
str[0] = 'H';
str[1] = 'e';
str[2] = 'l';
str[3] = 'l';
str[4] = 'o';
str[5] = '\0';이와 같이 문자열을 배열에 저장하기 위해서는 결국 한 글자 한 글자씩 배열에 저장하는 수밖에 없다. 그렇지만 매번 이렇게 한다는 것은 정말 번거로운 일이다. 이런 일을 해주는, 즉 문자열을 배열에 복사해주는 함수를 만들어두면 편할 것이다. 실제로 <string.h>에 선언되어 있는 strcpy() 함수가 이런 일을 해준다.
char *strcpy(char *buffer, const char *str);strcpy() 함수의 원형은 위와 같다. 두 매개 변수, 즉 저장시킬 배열 buffer와 문자열 str을 넘겨주면 str 문자열을 배열 buffer에 복사한다. 하지만, 한번쯤은 이 함수가 어떻게 동작하는지 생각해보거나 직접 만들어보는 것도 좋을 것이다. 만들어보면 문자열이 어떤 식으로 사용되는지 확실히 알 수 있을 것이다.
직접 해보고 고민해보는 것이 이해를 빨리 할 수 있는 방법이다. 실패하는 걸 두려워하지 마시고, 시도해보자.