본문 바로가기

프로그래밍./C언어.

[C언어] scanf의 기본과 문자 입력 안될 때 해결 방법.

[C언어] scanf의 기본과 문자 입력 안될 때 해결 방법.
[C언어] scanf의 기본과 문자 입력 무시 해결 방법.



기본적으로 scanf는 입력 받은 자료 수를 반환한다.
일반적으로 쓰는 경우는 거의 없기 때문에 본적은 없을지도 모른다.

scanf는 값을 입력받아서 scanf( , ) 
콤마 좌측에는 입력 받을 값의 형식을 지정해 준다. ex) 정수(%d), 실수(%lf), 문자(%c), 문자열(%s) 등등
우측에 지정해준 변수의 "주소" 에 내가 입력 받은 값을 저장한다. 따라서 변수의 주소를 추출하는 & 연산자가 필요.

지정한 형식과 변수의주소는 정확히 1:1 매치 되어야 한다. 
형식을 3개를 적어주고는 변수를 3개 보다 적게 또는 많게 적어주면 문제가 발생한다.

특히나 scanf는 "변수의 주소"를 넣어주어야 하는데 & 빼먹고 변수만 적어주면 메모리 오류가 발생한다.

내가 5를 입력했다면. a라는 변수가 메모리에 어디에 있는지 주소를 알려주고 
그 곳에 5를 복사해야 하는데, 변수만 적어주면 변수에 있던 값을 주소로 착각하고 그 곳에 가게 되는데
이 때 지정되지 않은 메모리 영역을 침범하기 때문에 에러를 발생한다.




형식을 지정해 줄 때는 " " 안에 입력해 주어야 하며,
여러개를 입력 받는 경우 값들을 구분하는 것을 "구분자"라고 하며
위 처럼 2개의 값 사이에 구분자로 공백을 주었다면, 값 입력 시 형식과 동일하게 값 중간에
스페이스, 탭 또는 엔터를 입력하면 해당 값은 제외 되고 입력 받는다.





하지만 지정한 값과 다른 값이 들어갔다면. 즉 형식과 맞지 않다면 뒷부분의 값들이 저장되지 않는다.
그림처럼 입력한 값이 1개 밖에 되지 않는다.
이런 원리를 이용해 scanf의 반환값으로 내가 원하던 대로 scanf가 작동 했는지도 확인할 수 있다.




 
scanf 함수는 시스템버퍼를 사용하며. 한줄짜리 행버퍼로 큐구조 이다.
즉, 먼저 들어온 값이 먼저 사용된다는 의미이다.

여기서 고려해야 할 아주 중요한 사항이 있는데 만약 2 와 3을 입력하고 엔터키를 눌렀다면.


버퍼(임시저장소)에는 2 와 3 그리고 엔터키 까지 저장되어 있다.
그리곤 변수에 2와 3이 들어가고 함수가 끝나는데 엔터키는 여전히 버퍼에 남아있게 된다.

프로그램이 종료되지 않은 상태에서 한번 더 scanf를 하게 되면 그 이후의 버퍼에 저장하게 되고
엔터키가 변수에 들어가게 되는 것이다. %d나 숫자를 입력 받는다고 형식을 지정하였다면 무시되지만
만약 %c 를 사용했다면. 즉 문자를 입력 받는다고 했다면 엔터키가 자동으로 들어가게 되는 것이다.


난 아직 입력하지도 않았는데 그냥 휙 넘아가 버린다.
에러도 아니고 아무런 오류가 나타나지 않는다.
다만 난 입력도 못하고 그냥 지나가 버린다는 것.
위의 구조를 모른다면 수정하는 무척 고생하게 될 것이다.





위 그림에서 5를 입력하고 엔터키를 누르자 5가 입력되고 다음 코드인 문자 입력이 그냥 넘어가 버렸다.
그리곤 변수들의 값을 출력하는데 마지막에 문자 입력에서 한줄 공백이 있는 것을 볼 수 있다.
원래는 값을 찍고 \n으로 한줄 내린 것이지만. 값 자체가 엔터키라 한줄 내려가고. 거기에 \n 때문에 또 내려간 것.

이런 경우는 여러 상황에서 발생할 수 있는데. scanf로 입력을 받고 버퍼에 값이 남아 있는 상태에서
%c로 한 문자만 받는 경우들 이다. 위에서 정수던 문자던 무얼 받았던 그 후에 %c를 사용하면 이런 사태가 발생한다.
주로 문자열(%s)로 긴 문자를 입력 받은 후에 %c를 하는 경우이다.




이런 상태를 해결하는 3가지 방법이 있다.


첫번째, scanf(" %c", &ch); 이렇게 %c 앞에 공백자체를 넣어주면 남겨진 공백이 무시된다.
위에서 설명했듯 %c 형식 지정 앞에 있는 문자는 그 자체 문자가 들어오면 무시한다고 했다.
공백은 스페이스와 엔터키 모두 가능하다. 따라서 마지막 엔터키가 " "의 공백으로 인식되 무시되고
그 다음에 문자를 입력 받으려고 커서를 깜빡이게 된다.

scanf("%d %c %s", &num, &ch, str); 는 첫번째 이유에서 사용이 가능하다.





두 번째, fflush(stdin);     입력 버퍼를 방출시킨다. 입력버퍼 비우기.
시스템 버퍼의 값을 지우는 함수이다. 남아 있던 엔터키나 나머지 값을을 날려 버린다.
입력 받은 다음, 혹은 다음에 입력 받을 곳 위에 위치해 준다.
stdin은 표준 입력 버퍼를 의미하며 scanf는 stdin을 사용한다.

문자열을 입력 받은 후 문자(%c)를 받아야 하는 경우라면 아래와 같은 방법도 있다.
scanf 함수가 아닌 gets(배열명); 를 사용하는 방법이다. 기본적으로 문자열은 마지막에 null 값이 들어가야 한다.
gets 함수는 마지막 엔터키를 null로 저장해 버린다. 그렇기 때문에 버퍼에 엔터키가 남지 않게 되는 것.
scanf의 경우는 %s라는 형식 지정자가 문자열 입력이 끝나면 알아서 null을 넣어준다. 그래서 엔터가 남는 것.



scanf와 printf처럼 gets도 한 쌍이 있는데 puts 이다.
puts는 gets 하면서 잃어버렸더 엔터키를 출력 후에 찍어준다. 그래서 출력 후 자동으로 한줄이 넘어간다.
printf는 값을 찍고 \n 직접 해줘야 하지만 puts은 자동으로.
그러다 보니 한줄 넘기지 않아야 할 곳에서는 - -;
보이다 싶이 \n 없지만 puts(str) 다음에 한줄이 내려가서
Press... 가 다음 줄에 출력 되어 있는 것을 볼 수 있다.

상황에 맞게 골라서 사용하자. gets와 puts도 시스템 버퍼를 사용하며 stdio.h에 포함되어 있다.


유용한 정보가 되셨다면 아래 손가락 버튼 한번 눌러주세요 ^-^