12-1.문자열 함수

12-1-가.문자열 복사

C언어에서 문자열은 널 종료 문자가 끝에 있는 문자 배열로 표현하며 기본 타입에 포함되지 않는다. 정수나 실수와 같은 기본 타입으로 인정되지 않기 때문에 컴파일러가 제공하는 =, ==, +, - 등의 기본적인 연산자를 자유롭게 사용할 수 없다. str1, str2, str3가 문자형 배열일 때 다음 코드는 모두 동작하지 않는다.

 

str1="Korea";                    // 문자열 상수를 대입할 수 없음

str1=str2;                         // 배열끼리도 대입할 수 없음

str3=str1+str2;                        // +연산자로 문자열을 연결할 수 없음

if (str1 == str2)                       // ==연산자로 문자열끼리 비교할 수 없음

 

대표적으로 대입의 문제에 대해 집중적으로 알아보자. char str[10]; 으로 문자형 배열을 선언하고 이 배열에 "korea"라는 문자열 상수를 대입하고 싶으면 char str[10]="korea";로 선언할 때 초기화해야 한다. 일단 선언되면 str="korea";로 대입할 수 없으며 str="china";로 내용을 바꿀 수도 없다. 그러나 선언 후 문자열을 대입하거나 내용을 중간에 바꾸어야 할 필요는 분명히 있으며 컴파일러가 이런 지원을 하지 않을 리가 만무하다.

이런 기능을 제공하는 것이 바로 문자열 함수들이다. 문자열이 기본 타입이 아니다 보니 정수나 실수처럼 연산자를 사용할 수는 없지만 문자열 함수를 사용하면 내용을 바꾸거나 비교, 연결하는 것은 물론이고 검색, 변환 등 연산자보다 훨씬 더 다양한 작업을 효율적으로 처리할 수 있다. 이 절에서는 문자열을 떡주무르듯이 자유 자재로 관리할 수 있는 문자열 함수에 대해 알아 볼 것이다. 문자열 함수들은 대부분 string.h에 선언되어 있으므로 이 헤더 파일을 인클루드해야 한다.

다음 함수는 문자 배열에 문자열을 복사하는데 문자열 함수 중에 가장 기본이 되는 함수이다. 문자 배열의 내용을 변경하므로 정수나 실수의 대입 연산자에 해당한다고 할 수 있다. 함수 원형에 const라는 지정자가 붙어 있는데 const는 함수가 인수의 내용을 바꾸지 않는다는 뜻이다. 다음에 상세하게 연구해 볼 것이므로 일단은 무시하고 없다고 생각해도 무방하다.

 

char *strcpy(char *dest, const char *src);

 

함수 이름 strcpy는 String Copy의 준말이므로 문자열 복사 함수라는 것을 쉽게 알 수 있다. 읽을 때도 "스트링카피"라고 읽는다. 두 개의 문자형 포인터를 인수로 취하는데 dest는 복사될 목적지이고 src는 복사될 원본이므로 이 함수가 하는 동작을 간단하게 표현하면 dest=src라고 할 수 있다. 즉, src 문자열이 dest로 복사되는 것이다. 이때 src의 제일 끝에 있는 널 종료 문자도 같이 dest로 복사된다.

리턴값으로는 dest의 번지가 다시 돌려지는데 특별한 의미는 없으며 잘 사용되지도 않는다. 이 함수를 사용하면 문자 배열을 선언한 후 문자열 상수를 복사할 수 있으며 언제든지 문자열의 내용을 바꿀 수 있다.

 

char str[10];

strcpy(str,"korea");

 

char str[10]; 선언에 의해 문자형 변수 10개를 저장할 수 있는 배열이 생성될 것이다. 별도의 초기값을 주지 않았으므로 이 배열은 쓰레기값으로 초기화된다. 이 상태에서 strcpy 함수로 "korea"라는 문자열 상수를 str로 복사하면 "korea"라는 문자들과 널 종료 문자까지 str 배열에 순서대로 복사된다. 이 함수의 인수로 사용된 "korea"라는 문자열 상수는 물론 정적 데이터 영역에 있을 것이다.

문자열의 끝을 나타내는 널 종료 문자까지 같이 복사되므로 복사 후 str 배열은 바로 사용할 수 있는 완전한 문자열이라 할 수 있다. 이 함수 호출 후 puts(str) 함수를 호출하면 str의 내용인 "korea"가 출력될 것이다. strcpy 함수의 두 번째 인수는 문자형 포인터이므로 문자열 상수외에 다른 문자열 배열을 줄 수도 있다. 다음 예는 이미 초기화된 str1을 str2로 복사한다. strcpy(str2,str1)에 의해 str1의 모든 문자들이(널 종료 문자까지 포함하여) str2로 복사되므로 이 함수 호출 후 str1과 str2의 내용은 완전히 같아진다.

 

char str1[]="Programmer";

char str2[11];

strcpy(str2,str1);

 

원래 배열의 끝 점검을 하지 않는 C언어의 특성상 strcpy 함수도 인수로 주어진 문자 배열의 끝 점검을 하지 않으며 이 배열의 크기가 얼마인지도 모른다. 그래서 strcpy 함수로 문자열을 복사할 때는 항상 dest가 src의 문자열을 대입받을만한 충분한 크기를 가지도록 해야 한다. 물론 널 종료 문자도 배열 크기에 당연히 포함시켜야 한다.

위 예에서 보듯이 'korea" 문자열을 복사받을 배열은 최소한 6바이트 이상 되어야 하며 "Programmer"라는 10문자를 저장할 배열은 최소한 11바이트 이상이어야 한다. 모자라는 것은 문제가 되지만 남는 것은 상관없으므로 문자열을 저장할 배열은 항상 크기를 넉넉하게 잡는 것이 좋다. 다음과 같이 배열의 길이가 충분하지 않을 경우 strcpy는 배열 뒤쪽의 메모리까지 덮어써 버릴 것이다.

 

char str[3];

strcpy(str,"korea");

 

str은 문자 3개를 저장할 수 있는 공간밖에 가지고 있지 않은데 이 좁은 공간에 "korea"라는 다섯 개의 문자와 널 종료 문자까지 복사했으므로 str에 인접해 있는 다른 변수가 파괴된다. 만약 이 메모리에 변수가 아닌 더 중요한 정보(예를 들어 리턴할 번지)가 저장되어 있다면 프로그램이 다운되는 치명적인 에러가 된다.

strcpy 함수는 src 문자열 전체를 널 종료 문자를 만날 때까지 dest로 복사한다. 이에 비해 다음 함수는 지정한 길이만큼만 복사한다.

 

char *strncpy(char *dest, const char *src, size_t count);

 

strcpy 함수의 원형과 유사하되 이름 중간에 개수를 지정할 수 있다는 의미의 알파벳 n이 삽입되었으며 세 번째 인수 count가 더 추가되어 있다. count는 복사할 문자 개수를 지정하는데 정확하게 이 개수만큼만 dest로 복사되며 널 종료 문자는 따로 덧붙이지 않는다. 다음 예를 보자.

 

: strncpy

#include <Turboc.h>

#include <string.h>

 

void main(void)

{

     char str1[10]="abcdefghi";

     char str2[10]="123456789";

     strncpy(str2,str1,3);

     puts(str2);

}

 

str1에는 알파벳 문자를 채워 놓고 str2에는 숫자를 채워 넣은 상태에서 str1의 문자 3개만 str2로 복사했다. 앞쪽 세 개의 문자만 복사되므로 str2는 "abc456789"로 바뀔 것이다.

strncpy 함수는 strcpy 함수와는 달리 지정한 개수만큼만 문자를 복사하므로 널 종료 문자가 개수에 포함되지 않으며 같이 복사되지 않는다. strncpy 함수로 3 문자만 복사하도록 했으므로  str 배열의 앞쪽부터 차례대로 a, b, c 문자만 str2로 복사될 뿐이다.

만약 count가 src 문자열의 길이보다 더 길다면 널 문자 이후는 모두 널 문자로 채워진다. str1이 "abcd"로 초기화되어 있는 상태에서 strncpy(str2,str1,10)을 호출하면 10문자분을 복사하되 str1[4]가 널 문자이므로 str2[4] 이후는 모두 널 문자로 채워진다. str1[5] 이후에 있는 쓰레기값이 복사되는 것이 아니다.

strcpy 함수는 문자열 전체를 완전히 복사할 때 사용하며 strncpy 함수는 문자열의 일부분을 다른 부분 문자열로 바꾸고 싶을 때 사용한다. 만약 문자열 시작 부분이 아니라 중간의 일부를 바꾸고 싶다면 복사할 시작 위치를 옮기면 된다. 다음 코드는 str1의 세 번째 문자 이후부터 3문자를 str2의 세 번째 문자 위치에 복사한다.

 

strncpy(str2+2,str1+2,3);

 

str1[2]부터 3문자분만큼 str2[2]로 복사되므로 str2는 "12cde6789"가 될 것이다. 포인터가 가리키는 시작 위치가 곧 문자열이기 때문에 +n만 하면 복사 대상 위치를 쉽게 바꿀 수 있다. 다음 예제는 strncpy 함수로 부분 문자열을 바꾼다.

 

: strncpy2

#include <Turboc.h>

#include <string.h>

 

void main(void)

{

     char str[]="김상형은 천재다.";

 

     strncpy(str+9,"바보",4);

     puts(str);

}

 

"천재"라는 단어가 str배열의 9번째 위치에서 시작하는데 이 위치에 있는 문자열을 "바보"라는 부분 문자열로 대체했다. 한글 한 음절은 2바이트를 차지하므로 "바보"의 길이는 4바이트가 된다. 출력되는 결과는 "김상형은 바보다."가 될 것이다.

다음은 문자열의 길이를 조사하는 strlen 함수를 알아보자. 문자열의 길이란 문자열 시작 번지에서부터 시작해서 널 종료 문자까지 들어 있는 문자의 개수를 의미한다. 널 종료 문자는 문자열의 끝을 나타낼 뿐 문자열의 일부가 아니므로 개수에는 제외된다.

 

size_t strlen(const char *string);

 

strlen은 String Length의 약자이며 문자열의 시작 번지만 주면 이 번지에 들어 있는 문자열의 길이를 조사한다.

 

char str[10]="abcd";

int len=strlen(str);

 

이 호출에 의해 len에 대입되는 값은 str 문자열의 길이 4가 될 것이다. 보다시피 str 배열에는 a, b, c, d 분명히 4개의 문자가 들어있지 않은가? 단, 한글 1문자는 2바이트를 차지하므로 한 음절이 2바이트로 계산된다. 그래서 strlen("대한민국")의 결과값은 4가 아니라 8이다. 이 함수가 조사하는 문자열의 길이는 배열에 실제 저장된 문자의 개수이지 배열의 크기가 아니다. 배열 자체의 크기를 구할 때는 sizeof 연산자를 사용해야 한다. sizeof(str)은 배열의 크기인 10을 리턴할 것이다.