scanf와 버퍼에 관해서

#include intmain(void) { charc1, c2; scanf("%c", &c1); scanf("%c", &c2); printf("c1 = %c, ", c1); printf("c2 = %c\n\n", c2); return0; }

위 코드를 입력 후 실행해보자. a와 b를 넣으려고 했을 때 a는 들어가지만 b값은 들어가지 않는 걸 확인할 수 있다.

왜 그럴까? %c와 %c 사이에 fflush(stdin)을 넣으면 해결되는 것을 알 수 있다.

그런데 신기한 건 scanf(“%c”, &c2); 를 scanf(“ %c”, &c2);로 바꿔줘도 해결이 된다는 것이다. 그리고 또 %c가 아닌 %d는 그대로 써줘도 잘 돌아간다.

천천히 살펴보자.

scanf는 특성이 있다. 에 내재되어 있는 함수로서 입력 버퍼에 있는 값을 가져와서 읽는다. 입력 버퍼란 키보드를 누를 때 그 문자들이 입력 버퍼에 임시로 들어가 있는 것이다.

신기한 건 정수형을 읽을 때와 문자형을 읽을 때가 다르다!

scanf 함수가 %d, 즉, 정수형을 읽을 때는 White space(공백문자)는 건너 뛰고 숫자를 읽어 온다. 공백문자에는 , '\t', '\n' 같은 것이 있고.

문자형이나 문자열은 다른 것이 white space도 하나의 문자로 읽어들인다는 것이 다르다. 그래서 scanf(“%c”, &c1); scanf(“%c”, &c2); 이렇게 적게되면 첫 번째에는 내가 원하는 문자 a가 들어가지만 엔터를 치기때문에 두 번째에 바로 엔터가 입력 버퍼에 들어가버린다. 그리고 바로 출력을 해버리니 a와 엔터인 공백이 나와버리는 것!

그런데 scanf(“ %c”, &c2);을 하게 되면 앞에 있는 스페이스 바가 내가 방금 쳤던 엔터를 먹어서 다시 새로운 문자를 칠 수 있는 것! 즉 scanf(“ %c “)처럼 공백이 들어가버리면 엔터키를 먹을 수 있는 것 같다. 신기한 건 공백을 앞에다 많이 쳐도 엔터키를 하나만 먹을 수 있나보다.

이걸 해결하는 방법은 공백이 엔터키를 먹은 것처럼 getchar()를 써서 입력 버퍼를 한 번 비워주거나(어차피 반환값이 들어갈 장소가 없으니 그냥 버려진다.) 아니면 완전히 비워주는 fflush(stdin);을 써주는 것도 좋다. 하지만 버퍼의 내용을 완전히 다 출력시키는 것이고 이 것은 윈도우 visual studio에서만 쓰이는 것이기 때문에 가능한 한 함수를 써서 while(getchar()!=’\n’); 지워주는 게 좋다.

while(getchar()!=’\n’);의 의미

getchar() 는 표준 입출력 스트림으로 한 문자를 입력받아서 반환해주는 함수다.

int getchar( void )

기능 : 키보드에서 문자 하나를 읽어 온다. 이 함수는 stdio.h에서 다음과 같이 정의되어 있는 매크로 함수이다. #define getchar() getc( stdin ) 기정 스트림인 표준 입력 스트림으로부터 한 문자를 읽어오는 함수임을 알 수 있다. stdin은 버퍼를 사용하기 때문에 getchar도 버퍼를 사용한다. 따라서 다음과 같은 특징이 있다. 1. 버퍼에 있는 문자를 꺼내 리턴하고 꺼낸 문자를 버퍼에서 지운다. 2. 버퍼가 비어 있으면 입력을 받되 Enter키가 들어올 때까지 입력을 받는다. 즉 문자 하나만 단순히 눌러서는 문자를 입력받을수 없다. 3. Ctrl+Z 입력시 Ctrl-Z를 EOF(-1)로 변환하여 출력한다.

getchar은 버퍼에서 캐릭터 하나를 가져옵니다.

예를들어 버퍼에 123’\n’ 있습니다. getchar()을 한번 호출하면 버퍼에서 1을 가져오고 이제 버퍼에는 23’\n’ 이 남게 됩니다.

while(getchar()!=’\n’); 를 하면 1,2,3, 을 버퍼에서 지우고 \n만 남게 됩니다. 그리고 마지막으로 ‘\n’의 값을 가져오면 while문의 값이 거짓이 되어 (‘\n’ != ‘\n’) 반복문을 빠져나오고, 버퍼에는 아무것도 남지 않게 됩니다.

위 while문에서 반복하고자 하는 것은 “버퍼에서 가져온 값이 \n 일때까지 getchar() 함수 호출 “ 라고 생각하시면 됩니다.

getchar를 호출하기만 해도 키보드 버퍼의 아스키 값 하나를 pop할 수 있음 \n를 만날 때까지 pop해서 버퍼를 비우겠다는 말임