C언어를 처음 공부하다보면 scanf로 입력을 받을 때 갑자기 입력을 안받고 넘어가버리는 경우가 있다. 예를 들어서
#include <stdio.h>
int main() {
int num;
char c;
printf("숫자를 입력하세요 : ");
scanf("%d", &num);
printf("문자를 입력하세요 : ");
scanf("%c", &c);
return 0;
}
이걸 실행하면 숫자만 입력받고 프로그램이 종료된다.
위의 경우 character를 입력받아야 할 부분에서 입력받지 않고 그냥 넘어가버리는 것을 확인할 수 있다. 숫자를 입력해야하는 부분을 문자열로 바꾸면 이번에는 문자열만 입력받고 character는 입력받지 않고 끝난다.
#include <stdio.h>
int main() {
char buf[30];
char c;
printf("문자열을 입력하세요 : ");
scanf("%s", buf);
printf("문자를 입력하세요 : ");
scanf("%c", &c);
return 0;
}
이런 경우가 왜 발생하는걸까. 우선 C언어에서 입력을 받는 구조를 알아야한다. 물을 받아서 담고 있는 물통이나 양동이처럼 입력을 받아서 담아주고 있는 녀석을 버퍼(buffer) 라고 부른다. 그리고 많은 버퍼 중에서도 키보드의 입력을 처리하는 버퍼를 stdin (흔히 입력 스트림) 이라 부른다. 두번째 코드 예시를 기준으로 우리가 kim을 입력하고 엔터를 누르게되면 stdin에는 다음과 같이 kim뒤에 개행 문자(\n)이 붙어서 들어가게 된다.
그리고 stdin 입력버퍼로부터 문자열을 얻어와야 한다. 정수를 얻어올 때도 마찬가지로 문자열을 얻어올 때는 공백 문자(' ', '\t', '\n')의 전까지 모두 가져온다. 그렇다면 위의 stdin에서는 kim까지만 가져오고 \n은 그대로 stdin에 남아있을 것이다. 즉, scanf가 stdin에서 숫자나 문자열을 가져올 때 공백 문자(' ', '\t', '\n')을 만나면 그 전까지가 끝이라고 생각해서 공백 문자 전까지만 가져오게된다. 따라서 문자열을 가져오고 난 뒤는 아래처럼 stdin은 \n만 남아있다.
그런데 또 중요한 것은 scanf로 character를 가져올 때(위에서 scanf('%c', &c); 처럼)는 공백 문자와 상관 없이 그냥 아무 문자 하나만을 가져온다. 그래서 위의 경우에 문자는 아무것도 입력받지 않고 끝나버린 것이다.
위에서 character만 공백 문자와 상관없이 문자를 가져온다고 했으면 숫자나 문자열은 어떻게 될까.
#include <stdio.h>
int main() {
char str[30];
int i;
scanf("%d", &i);
scanf("%s", str);
printf("정수: %d\n", i);
printf("str : %s", str);
return 0;
}
결과는
오... 문자를 입력받는 경우와 다르게 잘 받는다. 위의 경우도 정수를 입력할 때 3을 입력하고 엔터를 눌렀으므로 stdin에는 3\n이 들어가게 된다. 그리고 정수를 가져가고 stdin에는 \n만 남는다. 그리고 kim을 입력하고 엔터를 누르면 stdin에는 \nkim\n이 남게 된다. 하지만 문자열이나 정수에 대해 scanf로 입력받고자 할때는 원하는 형식의 데이터가 나오기 전까지는 공백문자(' ', '\t', '\n')들을 무시한다. 원하는 형식의 데이터가 나오고 나서 공백문자들을 만나면 공백문자 전까지의 데이터들을 가져간다. 따라서 맨앞의 \n은 무시하고 두번째 \n을 만나면 그 앞까지의 데이터 kim을 가져가서 위의 결과가 출력된다.
결론은 문자를 입력받을 때만 문제라는 것이다. 그렇다면 문자를 입력받을 때는 어떻게 해야 위와 같은 현상을 해결할 수 있을까? 간단하다. 문자를 입력받기 전에 특정 함수를 호출해서 stdin을 비우면 된다. 문자를 입력 받기 전에 특정 함수를 호출해서 표준 입출력을 비우는 방법은 여러가지가 있다.
1. getchar()
2. flush(stdin) 하지만 gcc에서는 안되고, Visual Studio같은 MS계열 컴파일러에서만 작동
Reference
'공부 > C || C++' 카테고리의 다른 글
C/C++ 파일 입출력 (0) | 2021.08.14 |
---|---|
C/C++ 함수 포인터를 반환하는 함수 (0) | 2021.08.06 |
C++ iterator를 reverse_iterator로 변환시 같은 element를 가리키지 않는 이유 (0) | 2021.07.30 |
C++ 11 universal reference, std::forward (0) | 2021.07.25 |
C++ 11 함수 객체(Functor)와 람다 표현식(Lambda Expression) (0) | 2021.07.24 |