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

Hyo Jun Bae님의 프로필 이미지
Hyo Jun Bae

작성한 질문수

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

14.26 함수 포인터의 배열 연습문제

while (scanf(

작성

·

1.1K

2

안녕하세요.

스트링 입력을 받는 아래 코드에서

while (scanf("%[^\n]%*c", input) != 1)

printf("Please try again.\n>> ");

아무것도 입력하지 않고 엔터키를 누르면

Please try again>> 

이 구문이 무한 반복으로 나타납니다.

왜 이런 걸까요?

답변 8

6

**오류 발견 시 지적 부탁드립니다.**

조금 까다로운 부분인데, 최대한 이해되도록 설명해보겠습니다.

scanf("%[^\n]%*c", input) 에서 빈 엔터를 입력하면 %[^\n]에 의해 scanf는 아무것도 읽어들이지 않습니다. \n 전까지 읽어야하는데, 첫 문자가 \n이니 작동하지 않는거죠.

따라서, while 문 검사에서 계속 빈 엔터를 마주치니, %[^\n]에 의해 scanf가 아무것도 읽어들이지 않고, 그렇기에 애초에 %*c 작동으로 이어지지 않는 겁니다.

그렇다면, %*c는 도대체 왜 필요할까?

아래의 단순한 코드를 실행해봅시다.

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main()

{

char input1[10];

scanf("%[^\n]", input1);

printf("%s checked", input1);

}

분명 이 코드에서는 scnaf 안에 %*c가 없습니다. 그러나 %*c가 있는 코드와 동일하게 동작합니다.

scanf 안을 바꿔가면서 실행해보세요

그런데 아래의 코드를 실행해보시면, %*c가 있고 없음에 따라 결과가 달라집니다

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

 

int main()

{

char input1[10];

char input2[10];

 

scanf("%[^\n]", input1);

scanf("%[^\n]", input2);

 

printf("%s checked", input1);

printf("%s checked", input2);

}

이 코드에서 abc를 입력하면 아래와 같은 결과가 나옵니다

이때 디버거로 주소를 확인해보면 아래와 같은 상태가 됩니다

즉, input1을 받고나서 버퍼에 남은 \n 때문에 input2를 받는 scanf가 제대로 실행되지 않고

출력시에 input1은 정상적으로 출력되지만 input2를 출력하려고 하니 문자열 끝의 \0 이 없고

쓰레기 값을 계속 출력하다가 input1의 메모리까지 침범해서 input1 끝의 \0을 만나서야 printf가 끝나는 모양새입니다

이 코드에서 원래와 같이 %[^\n]%*c 를 사용하면, input1 input2 모두 정상적으로 입력받을 수 있습니다.

따라서 정리하자면 이렇습니다

%*c는 버퍼에 남아있는 \n을 비워주는 역할이 맞다.

정확히는, 마지막 문자인 \n을 버퍼로부터 읽어들이지만, 없는셈 치고 문자열에는 입력해주지 않는 것입니다.

그러나,

[Enter]만 입력했을 경우 &[^\n]에 의해 scanf가 애초에 읽어들이지 않으므로 %*c가 작동하지 않았다는 것입니다.

-답변이 도움이 되셨다면 좋아요를 눌러주세요!-

6

**오류 발견 시 지적 부탁드립니다.**

넵 말씀하신대로 버퍼 때문이 맞습니다. 홍정모 교수님께서 왜 코드를 저렇게 쓰셨는지는 모르겠으나, 아마 엔터만 들어올 경우를 가정하지 않고 작성하신 것 아닐까요?

scanf가 작동하는 방식을 생각해봅시다. "%[^\n]%*c"에 따라 \n을 만날때 까지([^\n]) 읽어들이고, 마지막의 문자는 무시(*c)합니다.

이때 [Enter]만 입력하면?

먼저 버퍼에 \n이 쓰여질 겁니다. 동시에, scanf가 공백문자를 만났으므로 작동하기 시작합니다. 이때 scanf는 format specifier 에 따라 \n 만나기 전까지 읽고, 마지막 문자를 무시합니다. 

이때 \n 만나기 전까지 읽는다는 것은, \n을 읽는다는 것일까요, 버퍼에 남겨두는 것일까요?

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main()

{

char input[10];

char ch;

scanf("%[^'k']%*c", input);

printf("%s\n", input);

ch = getchar();

printf("%c\n", ch);

}

이런 코드를 예시로 실행해봅니다. 입력에 abck를 넣으면, abc가 먼저 출력되고, 한줄을 띄운 후 k가 출력됩니다

즉, 버퍼에는 k가 남습니다.

k 대신 \n 으로 실행해도 마찬가지입니다.

원래 코드로 돌아가, while 조건 검사 이후 printf가 실행되고, 다시 while문의 조건 검사를 하는데, 위와 똑같은 일이 반복됩니다. 버퍼에 있는 건 \n 뿐인데, [^\n]으로 인해 \n을 버퍼에 남기고 읽으니 반환값은 계속 0이고 무한루프를 돌 수 밖에 없습니다

해결책은, while 문안에 버퍼를 지우는 코드를 추가하면 됩니다.

-답변이 도움이 되셨다면 좋아요를 눌러주세요!-

3

부족한 제 설명이 도움이 됐다니 다행입니다 :)

2

Hyo Jun Bae님의 프로필 이미지
Hyo Jun Bae
질문자

많은 도움이 되었습니다. 보여주신 코드 공부하다보니 궁금했던 점을 넘어서 많은 부분을 배웠습니다. 감사합니다. 

2

엔터를 입력했을 때 scanf("%[^\n]%*c", input) 에서 반환값이 어떻게 될까요? 그리고 그 반환값에 따른 while문의 조건상태는?

1

사진까지 넣어주시니 진짜 공부가 많이 되었습니다! 감사합니다 Charlie Park님!

1

Hyo Jun Bae님의 프로필 이미지
Hyo Jun Bae
질문자

자세한 답변 정말 감사합니다.

한가지 여전히 의문으로 남는 부분은

%[^\n]%*c 코드에서

%*c이부분이 버퍼에 남아있는'\n'을 비워주는 역할로 알고 있는데, 왜 그렇게 하지 않았는가 입니다. 

0

Hyo Jun Bae님의 프로필 이미지
Hyo Jun Bae
질문자

답변 감사드립니다

반환값은 0으로 알고 있습니다. 따라서 while문 조건 ! =1에 해당되므로 다시 루프가 시작된다는 것 까진 이해하겠습니다.

하지만 다시 scanf입력을 할 수 있어야 하는데 그럴수 없이 출력이 무한 루프를 돕니다. 버퍼에 남은 정보때문인지 그부분은 명확이 이해를 하지 못하겠습니다. 

해당 부분의 프로그램 설계가 애초에 입력이 없으면 재입력을 받으려고 했던게 아니었나요?  

Hyo Jun Bae님의 프로필 이미지
Hyo Jun Bae

작성한 질문수

질문하기