개요
각종 알고리즘 또는 대회 문제를 풀다 보면, 공백으로 구분된 n개의 정수를 입력받아 배열에 저장해야 할 때가 있습니다.
scanf의 구조
일단 scanf는 콘솔의 입력 버퍼에서 문자열을 가져옵니다. 입력 버퍼는 콘솔에서 엔터키를 눌렀을 때 채워지는데 문자 하나를 읽어오는 게 아닐 경우, 즉 정수나 문자열 또는 실수를 읽어오는 경우에는 Whitespace (공백, 개행문자, 탭)가 아닌 문자가 나올 때 까지 Whitespace 문자들을 모두 버립니다. 그리고 Whitespace가 아닌 문자를 만나는 시점부터 진짜 입력이 시작됩니다.
입력이 이렇게 들어오고, 입력 버퍼에 저장됩니다.
123 45 67
그리고 프로그램에 이렇게 scanf가 사용 되었습니다.
int a,b,c;
//MSVC에서는 scanf 대신 scanf_s 함수를 사용하는 것이 권장됩니다.
scanf("%d", &a);
scanf("%d", &b);
scanf("%d", &c);
첫 scanf 문이 실행되면, 첫 문자를 검사합니다. 첫 문자는 "1"입니다. Whitespace가 아니므로 여기서부터 10진수로 인식 할 수 있는 한 최대한 많이 버퍼에서 읽어들입니다. 1, 2, 3을 차례대로 읽습니다. 그 다음에 나오는 공백문자 (Whitespace)는 10진수가 아니므로 읽기를 중단합니다. (공백 문자는 버퍼에 남겨집니다.) 그리고 읽어들인 수로 실제 값을 알아내서 변수 a에 저장합니다.
그리고 다음 scanf 문이 실행되면, 첫 문자를 검사합니다. 위에서 마지막 공백 문자(Whitespace)를 버퍼에 남겨두었기 때문에 첫 문자는 Whitespace이기 때문에 버립니다. (어디에도 저장되지 않고 버려집니다.) 그리고 다음 문자인 "4" 부터 버퍼에서 읽기를 시작합니다. 10진수로 인식되는 최대 문자까지 버퍼에서 읽어들입니다. 그리고 읽어들인 수로 실제 값을 알아내서 변수 b에 저장합니다.
3번째 scanf문은 두번째 scanf문과 똑같은 과정을 수행합니다.
실행 결과는 변수 a에는 123, 변수 b에는 45, 변수 c에는 67이 저장됩니다.
아래 C 코드는 정수 n을 입력받아 배열에 n개의 공백으로 구분된 정수를 입력하는 예제입니다.
#include <stdio.h>
int main()
{
int n;
//MSVC에서는 배열 이니셜라이져에 변수를 사용할 수 없다.
int arr[10];
scanf_s("%d", &n);
if(n < 10) return;
for(int i = 0; i < n; i++)
{
scanf_s("&d",&arr[i]);
}
return 0;
}
위의 코드에서 5개의 정수를 정확하게 1 2 3 4 5와 같은 식으로 입력하게 되면, 배열 arr의 0 - 5에 정확하게 1 2 3 4 5가 입력됩니다.
만약, 4개의 정수만 입력하게 될 경우, for 문에서 총 5번의 scanf를 통해 5개의 정수를 입력받아야 하지만 입력된 정수는 4개이기 때문에 마지막 scanf 문에서 읽어들일 정수가 없게 됩니다. 그러면 입력 버퍼를 대기하게 되어, 정수 하나를 더 입력할 때까지 프로그램이 대기하게 됩니다.
입력 버퍼 초기화하기
만약 위의 코드에서 5개 보다 더 많은 정수를 입력하게 될 경우, 5번의 scanf를 통해 5개의 정수를 입력받았지만 입력 버퍼에는 정수가 남아있게 됩니다. 그러면 해당 코드 밑에 scanf 문이 한번 더 있고 다른 값을 입력받으려고 의도한 경우, 버퍼에 남은 정수가 입력될 수 있습니다.
이와 같은 경우, 다음 입력을 받기 전에 입력 버퍼를 초기화 해줄 필요가 있습니다.
//MSVC 전용
fflush(stdin);
//GCC - MSVC 공용
rewind(stdin);
메서드로 입력 버퍼를 초기화할 수 있습니다.