일
KNK Chap.3 (3.2) 본문
scanf 함수 The scanf Function
printf 함수가 특정 서식을 따라 결과를 출력하는 것처럼, scanf도 특정 서식에 따라 입력값을 읽음.
scanf의 서식 문자열은 printf의 서식 문자열처럼 일반 문자와 변환 규격자 둘다 포함할 수 있음.
scanf에서 허용되는 변환들은 근본적으로 printf에서 쓰이는 변환들과 똑같음.
많은 경우, scanf의 서식 문자열은 다음 구문에서처럼 변환 규격자만 포함할 것임.
int i;
int j;
float x;
float y;
scanf("%d%d%f%f", &i, &j, &x, &y);
사용자가 아래와 같이 입력했다고 치면
1 -20 .3 -4.0e3
scanf는 이 줄을 읽고, 이 문자들을 각각이 뜻하는 숫자로 변환한 후,
1, -20, 0.3, -4000.0을 i, j, x, y에 각각 할당할 것임 .
"%d%d%f%f"처럼 "빽빽하게 채워진" 서식 문자열은 scanf 호출 시에 흔하게 나타남.
printf는 변환 규격자들의 간격이 그렇게까지 가까울 필요는 없음.
scanf는 printf와 마찬가지로, 부주의하면 빠질 수 있는 몇몇 함정들이 있음.
일단 변환 규격자의 개수와 입력 변수의 개수가 같은지 꼭 체크해야 하고,
각각의 변환이 해당 변수를 표현하기에 적절한지도 확인해야 함.
컴파일러가 printf에서처럼 개수 미스 매치 가능성에 대한 체크를 안해주기 때문.
또한 & 부호를 쓸 때도 조심해야 함. scanf 호출 시 보통 각 변수 앞에 붙는 부호라 중요함.
&는 항상은 아니라도 보통 꼭 필요하기 때문에, 프로그래밍을 할 때 까먹어서는 안됨.
[!!!] scanf 쓸 때 변수 앞에 & 부호를 빼먹으면 예측할 수 없는 안 좋은 결과가 나올 수 있음.
프로그램의 갑작스런 종료는 흔한 결과임. 일단 최소한 입력 때 받은 값은 변수에 저장 못함.
변수는 초기 값을 그대로 갖고 있을 것임. 초기 값 없다면 이상한 값을 받거나 아예 받지 못할 수도.
& 부호를 빼먹는 것은 아주 흔한 에러이니 꼭 주의 바람. 물론 어떤 컴파일러는 이 실수를 발견함.
그럴 경우 "서식 입력값이 포인터가 아닙니다format argument is not a pointer"라는 경고를 보냄.
포인터pointer는 chap 11에서 배울 것임. 변수에 포인터를 생성할 때도 & 부호가 쓰이기 때문.
scanf 호출은 강력하지만 데이터를 읽는 건 힘든 일임. 전문적인 C 프로그래머들은 잘 안 씀.
scanf 대신 모든 데이터를 문자 형식으로 받아 나중에 숫자 형식으로 전환함.
scanf를 쓰면 숫자를 읽는게 간편하기 때문에 책 초반에는 scanf를 꽤 자주 이용할 것임.
단, 이 책의 프로그램 대부분은 사용자가 이상하게 입력하면 제대로 작동하지 않으니 주의해야 함.
나중엔 scanf가 요청된 데이터를 제대로 읽었는지를 검사하고, 잘 못했다면 그걸 복구하는 방법을
작동하게 만들어 줄 수 있음. 그런데 이런 검사는 코드가 너무 길어지고 예제의 핵심이 무색해져서
책 내 프로그램들에는 비현실적이라 실제로 배우진 않을 것임.
scanf 의 작동 방식 How scanf Works
scanf는 사실 지금까지 말한 것보다 더 능력이 많음. 쉽게 말해 "짝 맞추기pattern-matching" 함수임.
즉, 입력된 문자들과 변환 규격자들을 짝지어 맞춰주는 것임.
printf처럼 scanf 또한 서식 문자열의 지시를 따름. 호출되면 일단 문자열 왼쪽부터 전처리를 시작함.
각각의 변환 규격자들을 위해, scanf는 적절한 형식의 아이템을 입력 변수에 넣는 것을 시도함.
이 과정에서 빈 칸은 필요시 무시함. 그 후 scanf는 그 아이템을 읽고, 그 아이템과 함께 속할 수 없는
문자를 만나면 멈춤. 아이템 읽기가 잘 끝나면, 서식 문자열의 나머지에 전처리를 계속함.
어떤 아이템이든 제대로 읽히지 않았다면, 나머지 부분은 읽지도 않고 즉시 값을 반환함.
→ 원서를 보면서 공부하는게 너무 오래 걸려서, 고민하다가 그냥 번역본으로만 공부하되 이해가 어려운 부분은
여러 자료를 찾아보면서 심화 공부하기로 했다. 그래서 앞으로는 중요 개념 필기 및 실습 기록 느낌으로 작성할 듯.
숫자를 입력받을 때 scanf()는 공란문자white-space characters를 무시함.
예시 ↓
scanf("%d%d%f%f", &i, &j, &x, &y);
이 프로그램에
1
-20.3
-40e3
을 차례로 입력해도
scanf()는 이것을 연속된 문자로 인식함
●●1¤-20●●●.3¤●●●-4.0e3¤
●는 빈 칸을, ¤은 개행문자를 의미함
그러니까 이걸 무시skip한 문자를 s, 읽은read 문자를 r로 표현하면
ssrsrrrsssrrssssrrrrrr
그렇다면 정수나 고정소수는 어떻게 인식하나?
정수일 경우 먼저 음양을 확인하고, 문자를 인식할 때까지 값을 읽음.
고정소수일 경우 음양 확인, 연속된 숫자 확인, 지수 확인.
%e, %f, %g는 scanf()와 함께 쓸 경우 상호 변환 가능.
즉, 이 세가지 변환 규격들은 scanf()의 고정 소수 인식 규칙을 따름.
그럼 scanf()가 현재 읽고 있는 아이템에 속할 수 없는 문자를 읽으면?
해당 문자를 뒤로 밀어두고 다음 아이템을 찾거나, 다음 scanf() 호출까지 대기함.
예를 들어 1, -20, .3-, 4.0e3¤ 이 네 개의 이상한 숫자를 통해 보면
scanf("%d%d%f%f", &i, &j, &x, &y);
scanf()는 이것들을 이렇게 처리함
○변환규격 %d: 1 읽음. -읽음. -는 정수에 포함될 수 없으니 1을 i에 저장. -는 뒤로 미룸.
○변환규격 %d: 다시 - 읽음. 2 읽음. 0 읽음. . 읽음. -20을 j에 저장. .은 뒤로 미룸.
○변환규격 %f: . 읽음. 3 읽음. - 읽음. 0.3을 x에 저장. -는 뒤로 미룸.
○변환규격 %f: -, 4, ., 0, e, 3, ¤ 읽음. -4.0 × 10³을 y에 저장. ¤는 뒤로 미룸.
→보다시피 scanf()는 서식 문자열의 각 변환 규격마다 아이템을 짝지어줄 수 있음.
개행문자 ¤는 읽히지 않아 다음 scanf() 호출 때 사용됨.
형식 문자열에서의 기본적인 문자들
"짝 맞추기" 개념은 서식문자열 내 변환 규격만 포함하는게 아니라 일반 문자 또한 포함한다.
scanf()가 일반 문자를 처리하는 방식은 두 가지다.
공란 문자일 경우: 뒤로 미룰 비공란 문자를 찾기 전까지 공란문자를 읽음. 공란문자끼리 짝지음.
기타 문자일 경우: 앞뒤 두문자가 짝지어질 수 있는지 비교함. 가능한 경우 다음 입력 문자 무시하고
서식문자열 확인. 가능하지 않은 경우 해당 문자를 다시 입력에 돌려놓고 읽기 과정 취소해
서식문자열을 처리하거나 입력 문자들을 더 이상 읽지 않음.
→이게 무슨 소리냐면, 예시를 보자.
서식 문자열이 "%d%d"라고 치고,
입력이 ●5/●96 라고 치자.
scanf()는 정수를 찾아야 하니까 첫번째 빈칸 무시하고 %d를 5와 짝지음.
다음으로 / 와 / 를 짝지음. 그 후 다시 빈 칸 무시하고 %d와 96을 짝지음.
이번엔 입력이 ●5●/●96 라면?
첫 빈칸을 넘기고 %d를 5와 짝지음. 그 후 / 를 짝지어야 하는데, 입력 문자가 빈칸이니 뒤로 미룸.
나머지 ●/●96 는 다음 scanf()가 읽게 놔둠.
만약 첫번째 정수 후 빈 칸을 허용하려면 서식 문자열을 "%d /%d"로 해야 함.
prinf와 scanf의 구분
둘은 비슷해 보이지만 차이점을 잘 숙지해야 함.
첫번째로, printf() 호출에 사용되는 변수 앞에는 &을 붙이면 안 됨. 예시 ↓
printf("%d%d\n", &i, &j); /*** WRONG ***/
실행하면 i와 j 값이 아닌 이상한 숫자 2개가 생길 것임.
두번째로, scanf()의 서식문자열 내에는 변환규격자 외의 문자를 대부분 추가하면 안됨.
printf()의 서식문자열에는 그래도 됨. scanf()에 그러면 예상치 못한 결과 나올 수 있음. 예시↓
scanf("%d, %d", &i, &j);
scanf()는 정수값을 찾아 i에 저장함. 근데 , 를 짝지어줘야 함.
그러려면 다음 입력 문자도 , 여야 함.
만약 아니라면? j의 값은 읽지도 않고 scanf()는 종료됨.
[!!!] printf()의 서식문자열이 \n으로 주로 끝난다고 해서 scanf()도 그렇게 끝내면 안됨.
scanf()에게 개행문자는 공란 문자와 같음. 그래서 다음 비공란 문자를 찾게 됨.
즉 사용자가 비공란 문자를 입력해줄 때까지 scanf()는 종료되지 않고 기다릴 것임.
[프로그래밍] 분수 더하기
scanf()의 짝 맞추기 능력을 살펴볼 예제임.
분수는 보통 분수numerator/분모denominator 꼴로 작성됨.
물론 아예 전체 분수를 입력해도 scanf()는 인식 가능.
이 프로그램은 scanf()가 어떤 식으로 분수를 한 번에 입력받을 수 있는지 보여줌.
이 프로그램을 실행하면 이렇게 나온다.
결과 값이 약분되진 않는다.
'C' 카테고리의 다른 글
KNK Chap.4 (4.1) (0) | 2022.12.14 |
---|---|
KNK Chap.3 (Q&A) (0) | 2022.12.14 |
KNK Chap.3 (3.1) (1) | 2022.12.07 |
KNK Chap.2 (2.8~Q&A) (0) | 2022.12.06 |
KNK Chap.2 (2.5~2.7) (0) | 2022.12.03 |