5.5 문자 포인터와 함수(Character Pointers and Functions)
"I am a string" 이라는 문자열은 문자들로 이루어진 배열이다. 문자열은 항상 NULL 문자,즉 '\0'으로 끝나게 되어있으므로 실제 저장되는 문자의 수는 우리가 보는것보다 한자 더많다
함수의 인수에 문자열이 나타나느경우가 많은데 예를 들면 다음과 같은 것이다.
printf("hello, world\n");
이런문장이 있으면 printf함수는 문자 배열의 시작점을 포인터로 받는다 문자배열이 매개변수로 쓰이지 않는 경우도 많은데 예를 들어
char *pmessage;
와 같이 선언되면
pmessage = "now is the time ";
등과 같은 문장을 사용할수있다. 이 문장은 pmessage라는 포인터에 now~time 문자열의 시작 번지를 넣어준다 C에서 문자열을 처리할 때는 이와같이 문자열 전체를 처리하는 것이 아니라 포인터만 사용한다.
다음의 두 정의 사이에는 중요한 차이가 있다.
char amessage[] = "now is the time "; /* an array */
char *pmessage = "now is the time"; /* a pointer */
amessage는 배열인데 끝에 '\0'이 있고 각각의 문자를 바꿀수도 있지만 항상 일정한 장소에 저장된다. 반면에 pmessage는 포인트 이므로 가리키는 위치를 바꿀수 있지만 내용을 바꾸는 것은 정의되어 있지 않다(바꿀수 없다는 말은 아니다)
포인터와 배열에 대해 좀더 알아보기 위해 표준 라이브러리에 있는 함수 두개를 살펴보자 첫번째 함수는 문자열 t룰 문자열 s로 복사하는 strcpy(s,t)이다 s=t라고 하면 되지만 이것은 문자열의 복사가 아니라 포인터를 같게 해준다는 말이다 배열을 이용하여 strcpy 함수를 작성하면 다음과 같다
/* strcpy: copy t to s; array subscript version */
void strcpy(char *s,char *t)
{
int i;
i=0;
while((s[i] = t[i]) != '\0')
i++;
}
포인터를 이용하여 strcpy 함수를 작성하면 다음과 같이된다.
/* strcpy: copy t to s; pointer version 1 */
void strcpy(char *s,char *t)
{
while((*s = *t) != '\0') {
s++;
t++;
}
}
매개변수는 그 값만 전달되므로(call by value) strcpy 내에서는 s와 t를 마음대로 사용할수 있다. s와 t는 매개변수이므로 초시값을 가진 포인터이며 문자의 순서를 따라 '\0'을 만날때 까지 복사가 이루어진다.
실제로 stcpy 함수는 위와같이 되어있지 않으며,전문가가 작성한 프로그램은 다음과 같이 될것이다
/* strcpy: copy t to s; pointer version 2 */
void strcpy(char *s+char *t)
{
while((*s++ = *t++) != '\0') {
;
}
}
이 프로그램에서는 s와 t를 증가시키는 부분이 루프의 비교하는 부분에 들어있다
*t++는 t가 증가되기 전에 가리키고 있던 문자이다 왜냐하면 ++가 뒤에 붙어있으면 t를 사용한후 t가 증가되기 때문이다 s도 똑같은 동작을 한다 '\0'도 역시 하나의 문자이므로 t에서 s로 문자열이 복사될때 '\0'도 복사된다.
위의 전문가가 작성한 프로그램도 더 간략화 할수있는 여지를 가지고 있다 생각해보면 '\0'과 같은지 검사할 필요가 없음 알수있다(문자가 0이면 while의 괄호 속에 연산결과가 0이 되므로 while문이 실행되지 않는다) 그러므로 프로그램은 다음과 같이 될수 있다.
/* strcpy: copy t to s; pointer version 3 */
void strcpy(char *s+char *t)
{
while(*s++ = *t++) {
;
}
}
이 프로그램은 처음 볼때 이상하게 보일지 모르지만 C에서 자주 사용되는 편리한 표현이므로 숙달하기를 권한다. <string.h> 라이브러리에 있는 strcpy 함수는 복사된 문자열을 리턴해준다
다음으로 공부할 프로그램은 strcmp(s,t)이다. 이 함수는 문자열 s와 t를 비교하여(사전적 순서)s보다 앞의 것이냐 같으냐 뒤의것이냐에 따라 +,0의 값을 리턴한다 리턴값을 얻는 방법은 두 문자열의 첫번째 문자와 비교하여 처음으로 다른 문자가 나왔을 때 그 문자값을 빼는 것이다. 프로그램은 다음과 같고
/* strcmp: return < 0 if s<t,0 if s=t,>0 if s>t */
int strcmp(char *s,char *t)
{
int i;
for(i=0;s[i] == t[i];i++)
if(s[i]=='\0')
return 0;
return s[i] - t[i];
}
포인터를 사용하면 다음과 같다
/* strcmp: return < 0 if s<t,0 if s=t,>0 if s>t */
int strcmp(char *s,char *t)
{
for(; *s == *t; s++ t++)
if(*s == '\0')
return 0;
return *s - *t;
}
연산자 ++와 -- 는 변수의 앞에도 붙을수 있고 뒤에도 붙일수 있기 때문에 *와 함께 사용하는 경우도 두가지 있다 예를들어
*--p
와 같이 하면 *p를 사용하기 전에 1증가시킨다. 다음과 같은 두줄
*p++ = val; /* push val onto stack */
val = *--p; /* pop top of stack into val */
은 스택을 push하고 pop 하는데 많이 사용한다 이는 4.3을 참조하자
헤더인 <string.h> 는 이 저에서 설명한 프로그램(함수)을 포함한 많은 문자열 처리 함수의 선언을 담고있다.
예제 5-3 제 2장에 있는 함수인 strcat(s,t) 문자열 t를 s의 끝에 첨가하는 함수 를 포인터를 사용한 함수로 바꾸어 보자.
예제 5-4 문자열 t가 문자열 s의 끝에 있으면 1을 그렇지 않으면 0을 리턴하는 함수 strend(s,t)를 작성하라
예제 5-5 라이브러리 함수인 strncpy,strncat,strncmp 함수를 함수의 매개변수로 들어온느 문자열의 처음 n문자에 동작하도록 바꾸어 보라. 예를들어 strncpy(s,t,n)는 문자열 t의 최대 n문자를 s에 복사하도록 한다.
예제 5-6 앞장에 나왔던 배열 사용 함수들을 포인터를 사용한 것으로 고쳐라 고칠만한 것은 다음과 같다 getline(제1,4장),atoi,itoa와 변형된 함수들(제2,3,4장),reverse(제3장),strindex와 getop(제 4장)