인프런 커뮤니티 질문&답변

홍길동님의 프로필 이미지

작성한 질문수

홍정모의 따라하며 배우는 C언어

8.5 숫자와 문자를 섞어서 입력받기

버퍼에 관한 질문 (수정본)

작성

·

638

2

Q1) A가 출력되지 않는 이유는?

영상 (02:40)에 나오듯이 scanf의 %c는 한 문자만 읽을 수 있음으로, "A 3 2"을 입력시, A만 호출되고 \n는 버퍼에 남겨지는 상태를 볼 수 있습니다. 그로 인하여, 다음 출력문으로 입력하였던 "B 1 2"대신에 (\n 3 2)가 먼저 출력되는 것을 확인할 수 있습니다.

여기서 우리는 실제 scanf(%c %d %d)버퍼에 입력된 값이 ('A', 3, 2, '\n')임을 알 수 있습니다.

  • scanf(%c %d %d) ↔  ('A', 3, 2, '\n')

그래서 다음과 같은 테스트를 진행해보았습니다.

영상(02:40)와 달라진 점은 기존에 \n자리에 s가 들어가고, 바로 그 뒤에 '\n'이 붙는다는 점입니다. 

즉, 현재 scanf(%c) 버퍼에는 'A', 's', '\n'가 들어가 있는 것이죠.

그렇다면 총 (A 3 2), (s 3 2), (\n 3 2)순으로 while문이 출력되어야 할 것이라는 것이 저의 예상이었습니다. 그러나 위 결과물처럼 'A'는 생략되고, 's'가 출력되는 것을 확인할 수 있습니다. 한편, 버퍼에 남아있던 '\n'는 잘 출력되는 것을 확인할 수 있습니다.

-----------------------------------------------------------------------------------

2) 영상 (05:10)의 오해. // 자문자답

(선생님의 코드와 동일합니다. "* 3 5 빈칸"을 입력하셔서 한 번 반복되신 것으로 확인됩니다. 아마 사람들은 빈칸을 못보고 지나간 다음에 스스로 테스트한 것과 달라서 의문이 생길 수 있으니 다음 분들은 참고해주세요!)

영상에 나온 코드로 "A 3 2"를 입력하면 while문은 종료하게 됩니다. 왜냐하면 while문의 조건이 (c = getchar()) != '\n')인데, 질문1처럼 'A'과 함께 '\n'도 같이 입력이 되니, while싸이클을 한 번 돌고, 다시 올라오면 '\n'와 만나서 반복되지 않고 프로그램은 종료되는 것을 확인할 수 있습니다.

영상에서 보여진대로, 이는 while문 중간에 while (getchar() != '\n') continue;를 넣어주면 해결할 수 있습니다.

그 이유는 A 3 2를 입력하였을 때,  getchar함수로 인하여 char c 변수에 'A' 문자 데이터가 저장되고, 버퍼에 남아있던 \n가 두 번째while문에서 읽혀지고 해당 while문 영역에서 벗어나면서 사라지기 때문입니다.

-----------------------------------------------------------------------------------

Q3) \n는 어디로??

본래, 저의 질문입니다.

getchar을 청소해주는 while문을 주석처리해놓고, "# 3 2 빈칸"과 "% 1 2"를 입력해보았습니다.

얼핏보면 당연한 결과물이라고 생각할수도 있으나, 질문2에서 확인했다시피 "# 3 2" 싸이클이 끝나면 해당 while문은 '빈칸 3 2'을 한번 더 돌리고, '\n'을 만나서 종료되어야 했습니다. 즉, '\n'의 다음 순서인 "% 1 2"는 실행되어서는 안됩니다.

"# 3 2 빈칸"을 입력하는 순간, 버퍼에는 ['#', 3, 2, '빈칸' '\n']가 저장이 됩니다.

이는 while문은 총 (# 3 2), (빈칸 3 2) (\n 3 2)를 돌릴 수 있는 경우의 수를 가지게 됩니다. 이 때, (\n 3 2)는 while문 조건에 의하여 종료되고 실행되지는 않아야 합니다.

***디버깅 테스트:

1. "# 3 2"가 출력된다.

2. 두 번째 싸이클인 "빈칸 3 2"이 시작되면 scanf함수가 실행이 됩니다.

3. scanf함수에 입력한 "% 1 2"는 버퍼에 들어가게 됩니다.

위 이미지처럼 char c변수에는 getchar함수로 인하여 '#'부터 '빈칸', '\n', '%'순으로 문자를 하나씩 버퍼에서 꺼내옵니다. 그 와중에 \n은 어디에서 제거가 된 것인가요?

답변 6

6

안녕하세요.

  1. A가 출력되지 않는 이유

As 3 2 라고 입력을 하셨기 때문에 c에는 A 가 들어갑니다. 근데 다음 문자인 s는 rows 에 들어갈 수 없습니다.  %d  즉 십진수의 정수만 rows에 입력할 수 있기 때문입니다. s는 문자 형태라 rows와 cols에 입력 될 수 없으므로 scanf는 c에만 'A'를 입력하고 십진수만 입력받기로 한 rows와 cols에는 's'를 입력하기를 포기하여 rows와 cols는 여전히 쓰레기값인 상태입니다. 's'를 버퍼에서 꺼내오는 것을 거부한 것이나 마찬가지기 때문에 s는 그대로 버퍼에 남아있습니다. (scanf 는 입력에 성공한 변수의 개수를 리턴하므로 scanf는 이때 1을 리턴했겠네요. c에만 입력을 했으니까요.) 

디버깅 해보시면 rows 와 cols에 입력 안된 것을 확인하실 수 있습니다. 이떄 입력 버퍼에는 's' '공백' '3' '공백' '2' '\n' 인 상태겠네요. 'A'만 입력이 되어 빠져나갔기 때문입니다.

그래서 두번째 while문 반복때 버퍼의 가장 앞에 있던 's' 가 c에 들어가는 것입니다.  c값은 원래 'A' 였는데 's' 가 입력되서 c값이 'A'에서 's'로 수정된거에요. 

첫번째 while문

👉 A만 변수c에 입력되고 rows, cols는 %d에 위반되는 's'를 받을 수 없어 입력 거부, c= A 상태. rows와 cols에는 입력되지 않아 쓰레기값 상태. 입력 버퍼는 's' '공백' '3' '공백' '2' '\n' 인 상태

👉 rows와 cols는 입력되지 못해 여전히 쓰레기값(음수인 -858993460)이기 때문에 음수라 i < rows 반복조건에 위반되어  for문이 실행되지 않고 넘어감 (1번의 답인 A가  출력되지 않은 이유)

두번째 while문

👉 입력 버퍼의 's' 가 c에 입력됨 (c 값은 'A'에서 's'로 변경) 버퍼에 있는 3 과 2 가 각각 rows와 cols에 성공적으로 입력될 수 있게 됨. (공백은 버퍼에서 빼고 버립니다.)

  1. 자문자답으로 적어놓으셔서 이 부분은 넘어가겠습니다. (이 부분에 대해서 답변글 쓴적이 있어서 링크합니다. https://www.inflearn.com/questions/100574)

3. "(# 3 2), (빈칸 3 2) (\n 3 2)를 돌릴 수 있는 경우의 수" -> 계속 이렇게 3 2 를 계속 언급하셔서 제가 헷갈리는데 3 2 는 이미 버퍼에서 빠져나가서 없어진지 오래입니다. 첫번째 while문 내에서 이미 rows cols 에 입력되면서 빠져나갔어요..! 

과정을 써보자면

"#공백3공백2공백" 입력 후 첫번째 while문이 다 끝나고나면

👉 버퍼는 [공백, \n] 상태겠죠. (#은 c에, 3은 row에, 2는 col에 입력되어 빠져나갔으니까요) 

이 상태에서 두번째 while문을 돌게되면

👉 공백은 문자이므로 getchar()로 입력될 수 있습니다. c에 공백이 입력되어 버퍼에서 빠져나가면서 버퍼는 [\n] 상태가 됩니다. c는 공백이라 \n이 아니므로 반복 조건이 참이 되어 while문 안에 들어갑니다.

두 번째 wihle문 내의 scanf

👉 \n는 십진수 숫자가 아니라 rows에 입력될 수 없을뿐더러 %c를 제외한 입력 지시자들은 공백을 버퍼에서 꺼내 무시합니다. (minq님 질문에 이부분도 제가 답변으로 언급해놨었습니다. \n이나 공백같은 whitespace는 버퍼에서 꺼내되 입력하지 않고 무시합니다. 👉👉👉 이 부분으로 3 번 답변이 되겠네요.)  그래서 버퍼 상태는 완전히 비어있게 됩니다. 더 이상 가져올게 없으니 scanf는 콘솔 입력창을 활성화합니다. 그래서 "% 1 2" 를 입력할 수 있었던 것이에요. 

👉 근데 % 또한 rows,cols 에 입력될 수가 없습니다.%는 whitespace가 아니라 버퍼에서 비워지진 않지만 %d 에는 부합하지 않는 것은 사실이므로 rows와 cols에 입력되지 못합니다. 그래서 rows와 cols에 입력하는 것을 거부하며 scanf 는 0 을 리턴하고 버퍼는  [% 공백 1 공백 2 \n ] 상태가 유지됩니다. %는 꺼내지지 않았습니다. rows와 cols는 첫번째 while문에서 각각 3과 2 값으로 입력이 됐었으므로 rows cols는 변동없이 3 과 2 값이 유지됩니다. 이번에 버퍼 앞에 % 가 있는지라 입력을 못받아서 수정이 안됐습니다. 

4. 정리해보자면

scanf("%d") 가 버퍼에서 whitespace가 아닌 일반 문자를 만난다면 👉 버퍼 상태가 [ s 2 3 ] 일 때 s는 입력되지 못하고 버퍼에서 꺼내지지도 않습니다. 그냥 scanf는 입력 자체를 포기하고 넘어갑니다. s 옆에있는 2를 입력하는 것이 아닙니다. (s는 버퍼에서 꺼내지지도 않았기 때문에 이런식으로 할 수가 없습니다. 자료구조 Queue 생각해보시면 되요) 

scanf("%d") 가 버퍼에서 whitespace(공백 \n \t 등등)를 만난다면 👉 버퍼 상태가 [공백 2 3] 일 때 공백은 버퍼에서 꺼냅니다. 그러나 아직 입력은 하지 않고 꺼낸 공백을 버립니다. 뒤이어 [2 3] 이 되어 2 는 %d에 부합하므로 잘 입력 합니다. 이렇게 공백이 아닌 문자를 만날때까지 공백을 버퍼에서 꺼내 버립니다. 설계 자체가 이렇게 되있는 듯 해요.

제가 과정을 장황하게 다 적어보긴 했는데 사실 디버깅 하시면 다 알 수 있는 부분입니다. (특히 1번) 

그러니 공부하실 때 디버깅 꼭 해보세요..! 😭

1

선배님들이 남겨놓으신 글덕분에 이해가 쉽게 갑니다

1

아하. 버퍼 상태가 (# 3 2), (빈칸 3 2), (\n 3 2) 이런 순서로 된다고 이해하고 계신건가 싶었습니다. 제가 잘 못 이해했네요!

"scanf(%d)에서 거절되도 다시 버퍼로 들어가게 됩니다." 👉 엄밀히 말하자면 다시 들어가는건 아닐거에요. 그냥 애초에 처음부터 버퍼에서 안꺼내진 것뿐! 버퍼를 큐랑 똑같이 생각해보시면 됩니다.

희한하게 C++ 들으신 후에 C들으시네요! 화이팅입니다.

0

진짜 제가 정확하게 궁금했던 질문에 감탄하고

또 성의 있고 시원하며 맥을 짚는 답변에 감탄했습니다.

감사합니다...

0

홍길동님의 프로필 이미지
홍길동
질문자

어디선가 C++이 C보다 쉬우니까 C++먼저 배우고 C배우는 것이 좋을 것이라는 말을 들어서 그렇게 됬네요 (웃음).

해당챕터까지 봐서는 C++이 C보다 훨~씬 어려운 것 같아요 ㅋㅋㅋ... (객체지향때문인가..)

잘 이해하다가 버퍼에 걸려서 질문을 드리게 되네요ㅋㅋ.

아무튼 화이팅하겠습니다!

감사합니다 joy님~!

0

홍길동님의 프로필 이미지
홍길동
질문자

joy님 소중한 시간 내어 답변해주셔서 정말 감사합니다!

그러게요... 디버깅하면 대부분의 문제를 해결할 수 있는데, 부주의했습니다!

1번 질문은 어쩌다보니 어제 시도해보고 안되니까 선뜻 질문으로 올렸던 것 같습니다 ㅋㅋ;;; (꾸벅..)

답변 4번, 바로 위에 "%는 꺼내지지 않았습니다. rows와 cols는 첫번째 while문에서 각각 3과 2 값으로 입력이 됐었으므로 rows cols는 변동없이 3 과 2 값이 유지됩니다. 이번에 버퍼 앞에 % 가 있는지라 입력을 못받아서 수정이 안됐습니다."

이 부분때문에 먼저 버퍼에 들어가 있는 '#', '빈칸', '\n'순으로 (# 3 2), (빈칸 3 2), (\n 3 2)의 경우의 수가 있겠구나 생각했던 것 같아요.

다시 말하여, 이 문제는 (# 3 2)의 while문이 끝나는 시점에서 버퍼에는 ('빈칸', '\n')이 남아있고, 빈칸이 출력되는 두 번째 while문에서 '빈칸'은 c = getchar로 c에 들어가지만, \n는 whitespace이니까 scanf(%d )에서 거절되고, 버퍼로 다시 돌아가지 않고 사라지게 됩니다. 이후 scanf을 통해 "% 1 2"를 입력 받으니 버퍼는 ('%', 1 2)가 남습니다.  여기서! %는 whitespace가 아니니까 scanf(%d)에서 거절되도 다시 버퍼로 들어가게 됩니다.

그래서 세 번째 while문에서 %는 다시 버퍼에서 c = getchar에 의해 c로 들어가게 되고, 1 2는 scanf(%d %d)로 각각 들어가 잘 출력된 것이군요!

나름 어제 많은 시간동안 어떻게하면 잘 질문할 수 있을까 고민하면서 이미지도 만들어보고 했는데, 역시 글로는 표현에 제한이 생기네요...

덕분에 개념 정리 잘하고 다음 챕터로 넘어갑니다!

감사합니다~!!!