작은숲:위키노트/초보자를 위한 C 언어 강의/문자열 입출력
먼저 문자열 입력에 대해 생각해보도록 하자.
문자열을 저장하기 위해서는 배열을 이용하는 경우가 많다. 그 이유는 배열을 이용해야, 아니 정확히 말해 할당된 메모리가 있어야 문자열을 입력받아 저장할 수 있기 때문이다.
문자열을 사용자에게 입력받기 위해 우리는 scanf() 함수나 gets() 함수를 이용할 수 있다.
char *str;
scanf("%s", str);
gets(str);위 예에서 포인터를 이용해 문자열을 입력받으려 하고 있다. 위와 같은 코드가 가능할까?
결론부터 말하면, 불가능하다. 왜? 포인터는 저장할 공간이 없기 때문이다. scanf() 함수나 gets() 함수를 통해 문자열을 입력 받아 어디에 저장해야 할까? 포인터는 단지 주소를 저장하는 변수이다. 즉, 어딘가 값이 저장된 곳을 가리키는 포인터이다. 자신에게 할당받은 공간이라고는 주소를 저장할 4 바이트(이것은 시스템에 따라 달라진다.) 밖에 없다. 만약 이렇게 포인터에 입력받은 문자열을 저장시키려고 하다가는 시스템 에러가 발생할 것이다.
그래서, 위의 코드는 아래와 같이 수정해줘야 한다.
char str[81];
scanf("%s", str);
gets(str);이렇게 배열을 선언하고(배열을 선언할 때는 반드시 크기에 신경 써야 한다.) 그 배열에 문자열을 저장한다. 복사하는 것과 같다.
char *gets(char *s);gets() 함수의 원형이다. 이 함수는 사용자가 엔터를 칠 때까지 입력을 받아 마지막에 들어오는 개행문자('\n')를 널 문자로 변환해서 배열 s에 저장해 준다. 배열이라기 보다는 할당된 메모리를 가리키는 포인터라는 말이 정확하다. 배열도 그런 포인터이니까.
만약 이 할당된 공간보다, 즉 배열의 크기보다 큰 문자열을 입력하면 어떻게 될까? 당연히 문제가 생긴다. 왜? 자신에게 할당된 공간이 아닌 남의 영역을 침입하게 되기 때문이다. 문제가 일어날 소지가 아주 다분하다. 따라서, 이 gets() 함수를 사용할 때는 아주 주의해야 한다. 아니면, 입력받을 문자열의 길이를 지정할 수 있는 fgets() 함수를 이용하는 게 낫다.
다음으로 문자열 출력을 생각해보도록 하자.
문자열 출력은 아주 간단하다. 문자열의 끝에는 뭐가 있을까? 그렇다. 바로 널 문자가 있다. 널 문자를 만날 때까지 한 글자씩 차례로 출력해주면 된다. 실제로 printf() 함수나 puts() 함수가 이렇게 동작한다.
char str[81] = "Hello";
printf("%s\n", str);
puts(str);이와 같이 문자열을 printf() 함수나 puts() 함수를 통해 출력할 수 있다. 이 함수들의 매개 변수로 넘어가는 것은 이 문자열이 시작하는 주소이다. 문자열 전체를 넘겨줄 수는 없으니까 그 문자열이 저장된 곳의 시작 주소를 넘겨주는 것이다. 함수들은 이 시작 주소에서부터 한 칸씩 이동하면서 문자열을 출력한다. 언제까지? 널 문자를 만날 때까지.
그리고, puts() 함수의 경우에는 마지막에 개행 문자를 하나 출력한다. 문자열에 개행 문자가 없더라도 추가로 붙여준다. 하지만, fputs() 함수는 개행 문자를 붙이지 않는다. 이것은 fputs() 함수와 puts() 함수의 차이이다.
이러한 함수를 이용하지 않고 문자열을 출력하는 건 어떻게 할까? 위에서 말했던 것처럼 널 문자를 만날 때까지 한 글자씩 출력해주면 된다.
#include <stdio.h>
int main()
{ int i;
char buffer[81] = "Hello";
for (i = 0; buffer[i]; i++)
putchar(buffer[i]);
return 0;
}위의 for 문에서 조건식으로 사용된 buffer[i] 라는 문장을 보자. 이 문장이 의미하는 바가 무엇일까? 바로 널 문자를 만날 때까지 반복문을 수행하라는 것이다. 즉, 널 문자('\0')는 아스키 코드 0을 가지기 때문에 이 값이 조건식에 쓰이게 되면 거짓이 됩니다. 거짓이 되면 반복문에서 빠져나오게 된다.
문자열에서는 널 문자만 생각하면 대부분의 문제를 해결할 수 있다.