티스토리 뷰

아마 C언어를 처음 배우실 때 가장 먼저 배우는 함수가 "Hello World!"를 출력하기 위한 printf() 함수고, 그 다음으로 많이 배우는 함수가 값을 입력받기 위한 scanf()가 아닐까 싶습니다. scanf()는 대표적인 입력 함수 중에 하나지만 문자열을 읽을 때는 주의할 점이 있습니다. 바로 한 단어만 읽는다는 것입니다. 즉, 스페이스바를 만나면 그 앞까지만 읽어 들입니다.


1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main(void) {
    char name[100];
 
    printf("이름을 입력해 주세요. ");
    scanf("%s", name);
 
    printf("당신의 이름은 %s입니다.\n", name);
 
    return 0;
}
cs


이 프로그램은 이름을 입력 받아서 그대로 출력합니다.


만약 "Feel Life"를 입력했다고 한다면 우리의 의도와는 다르게 "당신의 이름은 Feel입니다."라고 출력될 것입니다. scanf()로는 여러 단어를 읽을 수가 없기 때문에 이를 위해서 제공된 문자열 함수가 바로 gets()와 fgets()입니다.


char * gets(char * str)



먼저 gets()의 경우, 사용법이 매우 간단합니다. 단순히 매개변수로 문자열을 저장할 포인터를 전달하면 되고, 함수가 정상적으로 호출된 경우, 값이 저장된 포인터가 반환되고, 문자열을 읽는 데 실패했다면 NULL 포인터를 반환합니다.


1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main(void) {
    char name[100];
 
    printf("이름을 입력해 주세요. ");
    gets(name);
 
    printf("당신의 이름은 %s입니다.\n", name);
 
    return 0;
}
cs


위 예제에서 scanf()만 gets()로 바꿔봤습니다.


위와 같이 "Feel Life"를 입력한다면 scanf()를 사용했을 때와 달리 "당신의 이름은 Feel Life입니다."라고 출력됩니다. 이어서 fgets()를 간단히 알아본 후에 비교하면서 더 상세한 설명을 하겠습니다.


char *fgets(char *str, int n, FILE *stream)



fgets()도 매우 간단한 함수이지만 처음 보면 gets()에 비해선 약간 복잡해 보입니다. 첫 번째 매개변수는 gets()와 같이 문자열이 저장될 포인터, 두 번째는 입력받을 문자열의 길이, 세 번째는 입력받을 스트림을 지정합니다. gets()와 눈에 띄는 차이점으로는 매개변수의 개수 말고도 개행 문자를 저장하냐 안 하냐의 차이도 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main(void) {
    char name[100];
 
    printf("이름을 입력해 주세요. ");
    fgets(name, 100, stdin);
 
    printf("당신의 이름은 %s입니다.\n", name);
 
    return 0;
}
cs


이 프로그램에 "Feel Life"를 입력한다면 gets()를 이용했을 때와 달리 "당신의 이름은 Feel Life\n입니다."가 출력됩니다. gets()는 개행 문자를 만나면 읽고 버리는 반면 fgets()는 개행 문자까지 읽기 때문입니다.


여기까지는 눈에 보이는 차이고 사실 가장 큰 차이는 두 번째 매개변수입니다. 이 매개변수는 입력받을 문자열의 길이를 나타내는데 위 예제처럼 100을 입력하면 마지막 '\0'(null 문자)를 저장할 공간을 제외하고 최대 99 길이의 문자열을 입력받을 수 있습니다. 그런데 gets()를 보면 입력받을 문자열 길이에 대한 정보가 전혀 없습니다.


만약 값을 저장할 공간이 100인데 500 길이의 문자열이 입력되었다면 어떻게 반응할까요? fgets()는 개행 문자를 만나거나 (n - 1) 길이만큼 문자열이 입력될 때까지만 읽어 들이기 때문에 99개의 문자만 읽어 들이고, 남은 값들은 다음 입력 함수로 넘깁니다. 그런데 gets() 함수는 한계가 없습니다. 즉, 저장할 수 있는 최대 공간인 100을 넘어서까지 저장이 계속됩니다. 이를 버퍼 오버플로라 하며 할당받은 메모리의 범위를 넘어섭니다. 즉각적인 문제를 일으킬 가능성은 크지 않지만 위험한 방법이란 것에는 공감을 하실 겁니다.


gets()가 가진 이러한 문제는 많은 프로그래머들이 인식해서 표준에서 gets()를 삭제하자고 권유하기도 했습니다. C 표준을 만드는 위원회에서도 이를 인정하여 C99 표준에서는 이미 쓰여진 프로그램과의 호환성을 위해 표준에서 제외하지는 않았지만 사용 금지를 권했고, 최신 표준인 C11에서는 아예 표준에서 제외됐습니다.


정리하자면 gets() 함수는 최근 표준에서 제외될 정도로 문제가 많은 함수이므로 여러 단어를 입력받을 때는 fgets() 혹은 이를 가공하여 쓰자!

댓글