
scanf fgets 차이는 단순히 어떤 함수가 더 좋은가의 문제가 아닙니다. C 언어에서 `scanf`는 형식에 맞는 토큰을 읽는 데 강하고, `fgets`는 문자열 한 줄을 비교적 안전하게 읽는 데 적합합니다.
핵심은 문자열 입력에서는 버퍼 크기와 공백, 개행 처리를 직접 통제해야 한다는 점입니다. 이 글은 C 표준 라이브러리 문서와 POSIX 설명을 기준으로 `scanf`와 `fgets`의 차이를 예제로 정리합니다.

C scanf fgets 차이의 출발점
C에서 입력은 자동으로 안전해지지 않습니다. 배열 크기를 개발자가 정하고, 문자열 끝의 null character도 고려해야 합니다. 그래서 입력 함수 선택은 생각보다 중요합니다.
`scanf`는 형식에 맞춰 값을 읽는 함수입니다. 정수 하나, 실수 하나처럼 명확한 토큰 입력에는 유용합니다. 하지만 사용자가 입력한 한 줄 문자열을 그대로 받고 싶을 때는 예상과 다르게 동작하기 쉽습니다.
scanf(“%s”)는 공백에서 멈춘다
`scanf(“%s”, name)`은 문자열을 읽지만, 공백 문자를 만나면 입력을 멈춥니다. 그래서 이름에 공백이 들어가거나 문장을 읽고 싶은 경우에는 적합하지 않습니다.
#include <stdio.h>
int main(void) {
char name[20];
scanf("%19s", name);
printf("[%s]\n", name);
return 0;
}입력으로 `hello world`를 넣으면 `hello`까지만 name에 들어갑니다. `world`는 입력 버퍼에 남아 다음 입력에 영향을 줄 수 있습니다.
폭 지정 없는 scanf는 위험하다
`scanf(“%s”, name)`처럼 폭을 지정하지 않으면 배열 크기보다 긴 입력이 들어올 수 있습니다. C 문자열은 자동으로 크기를 늘려주지 않기 때문에 버퍼를 넘는 쓰기가 발생할 수 있습니다.
char name[20];
// 위험: 입력 길이를 제한하지 않는다
scanf("%s", name);
// 그나마 나은 형태: 최대 19글자 + '\0'
scanf("%19s", name);폭을 지정하면 위험이 줄지만, 여전히 공백 포함 문자열을 읽는 문제와 입력 버퍼에 남는 문자 문제는 따로 생각해야 합니다.
fgets는 버퍼 크기를 함께 받는다
`fgets`는 읽을 배열과 크기, 입력 스트림을 함께 받습니다. 지정한 크기 안에서 한 줄을 읽고 문자열 끝에 null character를 붙입니다.
#include <stdio.h>
int main(void) {
char line[100];
if (fgets(line, sizeof(line), stdin) != NULL) {
printf("[%s]\n", line);
}
return 0;
}이 방식은 공백을 포함한 문자열을 받을 수 있고, 버퍼 크기를 코드에서 확인하기 쉽습니다. 그래서 사용자 입력 한 줄을 받을 때는 `fgets`가 더 예측 가능한 선택이 되는 경우가 많습니다.
fgets의 개행 문자 처리
`fgets`는 줄 끝의 newline을 읽으면 그 문자를 버퍼에 포함합니다. 그래서 입력값 비교나 문자열 저장 전에 개행 문자를 제거해야 할 때가 많습니다.
#include <stdio.h>
#include <string.h>
int main(void) {
char line[100];
if (fgets(line, sizeof(line), stdin) != NULL) {
line[strcspn(line, "\n")] = '\0';
printf("[%s]\n", line);
}
return 0;
}`strcspn(line, “\n”)`은 문자열에서 개행 문자가 처음 나타나는 위치를 찾습니다. 그 위치에 `\0`을 넣으면 줄 끝 개행을 제거할 수 있습니다.
숫자는 fgets로 읽고 sscanf로 파싱할 수도 있다
입력 전체를 먼저 한 줄로 받고, 그 다음 `sscanf`로 필요한 값을 해석하면 입력과 파싱 단계를 분리할 수 있습니다. 오류 처리도 더 명확해집니다.
char line[100];
int age;
if (fgets(line, sizeof(line), stdin) != NULL) {
if (sscanf(line, "%d", &age) == 1) {
printf("age = %d\n", age);
} else {
printf("숫자를 입력해야 합니다.\n");
}
}이 방식은 입문 단계에서는 조금 길어 보이지만, 사용자가 잘못 입력했을 때 프로그램을 더 차분하게 처리할 수 있습니다.
언제 scanf를 써도 괜찮을까
- 문제 풀이에서 입력 형식이 엄격하게 정해져 있을 때
- 정수나 실수처럼 공백으로 구분되는 토큰을 빠르게 읽을 때
- 폭 지정과 반환값 확인을 함께 할 수 있을 때
- 사용자 자유 입력이 아니라 테스트 데이터 입력일 때
실무 체크리스트
- 문자열 한 줄을 받아야 하면 `fgets`를 먼저 검토한다
- 배열 크기를 `sizeof(buffer)`로 함께 넘긴다
- 필요하면 newline을 제거한다
- 숫자 변환은 `sscanf`나 `strtol`처럼 별도 단계로 나눈다
- `scanf`를 쓴다면 폭 지정과 반환값 확인을 빼먹지 않는다
정리
C에서 `scanf`와 `fgets` 중 하나가 항상 정답은 아닙니다. 다만 사용자 문자열 입력에서는 공백, 버퍼 크기, 개행 처리를 명확히 다룰 수 있는 `fgets`가 더 안전한 출발점이 되는 경우가 많습니다.
C 입력 감각은 알고리즘 입력 처리와도 연결됩니다. 그래프 문제는 입력부터 다르다, KMP 알고리즘의 pi 배열은 왜 필요한가, 0-1 BFS는 다익스트라와 무엇이 다를까를 함께 보면 입력 구조를 더 넓게 볼 수 있습니다. 외부 기준은 cppreference – scanf, cppreference – fgets, POSIX – fgets를 확인했습니다.