본문 바로가기
📚 STUDY/🔥 C

[3-2] scanf 함수

by 엄지잉 2024. 9. 2.

- printf가 특정 서식에 따라 출력하듯, scanf는 특정 서식에 따라 입력을 받음.

- scanf에서 허용되는 변환들은 전부 printf에서 사용되는 변환들과 같음.

 

int i;
int j;
float x;
float y;
scanf("%d%d%f%f", &i, &j, &x, &y);
  • 사용자 입력 : 1 -20 .3 -4.0e3
  • scanf는 이 줄을 읽은 다음 각 문자들을 내포하고 있는 숫자로 변환한 다음 i, j, x, y에 각각 1, -20, .3, -4.0e3을 할당함.
  • scanf를 호출할 때에 "%d%d%f%f"와 같이 "빽빽하게 채워진" 형식 문자열을 자주 사용하게 됨. (printf는 반면에 변환 규격들이 인접해있지 않는 경우가 많음)

 

❗ scanf는 자신도 모르게 실수할 때가 있기에 몇 가지 주의를 해야함❗

1. scanf를 사용할 때면, 프로그래머들은 반드시 변환 규격의 개수와 입력받는 변수의 개수가 같으며, 각 변환이 해당하는 변수에 알맞는지를 확인해야 함.

2. scanf를 호출할 때 사용하는 변수들 앞에 사용되는 & 기호는 보통 꼭 필요하며, 까먹으면 안 됨.

[!!!] scanf를 사용할 때 & 기호를 깜빡하고 적지 않는 경우에 예측할 수 없는 결과가 나오고, 어쩌면 좋지 못한 결과를 불러올 수도 있다. 프로그램이 오류가 나서 종료되는 것이 흔한 결과일 것이다. 가장 얌전한 결과는 입력 때 받은 값을 변수에 저장하지 못하고, 변수는 기존에 갖고 있던 값을 그대로 갖고 있을 것이다. 물론 만약 변수가 초기값을 갖지 않는 상태라면 그러한 결과는 나오지 않을 것이지만 말이다. 
& 기호를 적어주지 않는 행위는 사실 매우 흔한 에러이니, 꼭 주의하길 바란다. 어떤 컴파일러는 이 에러를 파악하고 "서식 입력값이 포인터가 아닙니다format argument is not a pointer"와 같은 경고 메시지를 낼 것이다. 여기서 포인터는 뒤에 나올 11 단원에서 다룰 것이다. & 기호가 변수에 포인터를 생성할 때 사용하기 때문이라고만 알아두라. 만약 이러한 경고 메시지를 보게 된다면, & 기호가 누락된 것이다.

 

- scanf를 호출하는 행위는 매우 강력하지만, 데이터를 읽기에는 좋지 못한 방식임. 대부분의 전문적인 C 프로그래머들은 scanf를 사용하지 않고, 모든 데이터를 문자 형식으로 받아 나중에 숫자 형식으로 변환하는 방식을 사용함.

 

 

📌 scanf의 작동 방식

- scanf 함수는 사실 변환 규격과 입력된 문자들을 짝짓는 "짝 맞추기" 함수임.

- printf 함수와 마찬가지로, scanf 함수는 서식 문자열에 의해 제어됨.

 

① scanf 함수는 호출될 때 문자열을 왼쪽부터 처리하는 것으로 시작함.

② scanf 함수는 서식 문자열 내 변환 규격마다 각각 알맞는 형식의 아이템을 입력되는 변수에 집어넣음.

③ scanf는 해당 아이템을 읽고, 해당 아이템과 무관한 문자가 나오면 읽는 것을 멈춤.

④ 읽기 완료되면, 그 다음엔 남은 나머지 서식 문자열을 처리함.

(어떤 아이템이라도 제대로 읽히지 않았다면, scanf 함수는 나머지 서식 문장은 확인도 하지 않고 즉시 값을 반환함)

 

- 기본적으로 숫자를 입력받는다고 할 때, scanf 함수는 공란 문자(빈칸, 수평/수직 탭, 폼-피드form-feed, 개행문자)를 무시함. 즉, 숫자는 한 줄에만 있을 수도 있고, 여러 줄에 걸쳐 적혀있을 수도 있음.

 

scanf("%d%d%f%f", &i, &j, &x, &y);

 

사용자가 본 프로그램에 다음과 같이 3줄을 입력했다고 가정하자.

●●1¤-20●●●.3¤●●●-4.0e3¤ ssrsrrrsssrrssssrrrrrr

1

-20 .3

-4.0e3

  • scanf함수는 이를 세 줄이 아닌 하나의 연속된 문자로 인식함. scanf 함수는 여기서 숫자를 읽어야 하므로, 숫자를 찾는 도중에 발견되는 모든 공란을 무시함. 그 결과, scanf 함수를 정상적으로 네 숫자를 읽을 수 있게 됨. (*s : skip한 문자, r : read한 문자)
  • scanf 함수는 마지막에 있는 개행문자를 읽지는 않지만 인식은 함. 이 개행문자는 다음 scanf 함수 호출 때 첫번째 인식될 문자임.

1-20.3-4.0e3¤

  • 변환규격: %d. 공란문자가 아닌 첫 문자는 1이다. 정수는 1로 시작할 수 있으므로 scanf 함수는 그 다음 문자인 -를 읽는다. - 문자는 정수에 포함될 수 없으므로 scanf 함수는 1을 i에 저장하고 -를 뒤로 미룬다.
  • 변환규격: %d. scanf 함수는 이제 다시 - 문자를 읽고, 순서대로 2, 0, .을 읽는다. 정수는 소수점을 갖고 있지 않으므로 scanf은 -20을 j에 저장하고 . 문자를 뒤로 미룬다.
  • 변환규격: %f. scanf 함수는 ., 3, -를 읽는다. 고정소수는 숫자 이후에 - 기호가 올 수 없으므로 0.3을 x에 저장하고 - 문자는 뒤로 미룬다.
  • 변환규격: %f. 마지막으로 scanf 함수는 -, 4, ., 0, e, 3과 ¤(개행문자)를 읽는다. 고정소수는 개행문자를 가질 수 없으므로 -4.0 × 10³을 y에 저장하고 개행문자를 뒤로 미룬다.

 

📌 형식 문자열에서의 기본적인 문자들

- scanf가 서식문자열 내의 일반적인 문자를 처리하는 방식은 그 문자가 공란인지 아닌지에 따라 달라짐.

  • 공란문자 : 서식 문자열 내에 하나 or 연속된 공란문자가 존재할 경우 scanf는 (뒤로 미룰) 비공란문자를 찾기 전까지 계속해서 공란문자를 읽음. 서식문자열에 공란문자가 몇 개 있는지 중요하지 않음. 서식 문자열 내의 한 개의 공란문자만으로도 입력받은 문자들 내 여러 개의 공란문자 전부와 짝지어짐.
  • 기타 문자들 : 서식 문자열 내의 비공란문자를 읽게되면 scanf는 이를 다음 입력문자와 비교함. 만약, 이 두 문자들이 짝지어질 수 있으면 scanf는 입력문자를 무시하고 다음 서식 문자열을 확인함. 짝지어지지 않는다면, scanf는 해당 문자를 다시 입력에 돌려놓고 읽기 과정을 취소해 서식 문자열을 처리하거나 입력 문자들을 더이상 읽지❌

서식 문자열이 "%d/%d"일 때

 

① 입력 :  ●5/●96

     scanf는 우선 정수를 찾아야하므로 첫 번째 빈칸을 무시하고%d와 5를 짝지어줌.

     그 다음엔 /를 /와 짝지어주고, 또 다시 빈칸을 무시하고 %d와 96을 짝지어줌.

 

② 입력 :  ●5●/●96

     첫 번째 빈칸을 넘기고 %d를 5와 짝지어줌.

     /를 짝지어줘야 하는데, 입력문자는 빈 칸이므로 빈 칸을 뒤로 물리고 나머지 ●/●96 은 다음 scanf 호출에 의해 읽힐 수 있게 내버려둠.

     만약, 첫 번째 정수 이후 빈 칸을 허용하려면 서식 문자열을 "%d /%d"로 해야함.

 

 

📌 printf와 scanf의 구분

- 가장 흔한 실수는 printf 함수 호출에 사용되는 변수 앞에 & 붙이는 것.

- scanf 서식 문자열을 printf 서식문자열 대하듯이 대하는 것

scanf("%d, %d", &i, &j);
  • scanf은 우선 정수값을 찾을 것이고, 이는 i 변수에 저장됨. 그 다음엔 ,을 짝 지어줘야하기 때문에 다음 입력문자는 쉼표여야함. 만약 다음 입력문자가 빈 칸이라면 거기서 j의 값은 읽지도 않은채로 scanf은 종료됨.

 

🔥 [프로그래밍] 분수 더하기

🌐 addfrac.c

/* 두 분수를 더해준다 */

#include <stdio.h>

int main(void)
{
    int num1;
    int denom1;
    int num2;
    int denom2;
    int result_num;
    int result_denom;

    printf("첫번째 분수를 입력하세요: ");
    scanf("%d/%d", &num1, &denom1);

    printf("두번째 분수를 입력하세요: ");
    scanf("%d/%d", &num2, &denom2);

    result_num = num1 * denom2 + num2 * denom1;
    result_denom = denom1 * denom2;

    printf("둘의 합은 %d/%d 입니다.\n", result_num, result_denom);

    return 0;
}
  • 이 프로그램을 실행해보면 다음과 같음.
    • 첫번째 분수를 입력하세요: 5/6
    • 두번째 분수를 입력하세요: 3/4
    • 둘의 합은 38/24 입니다.
  • 결과 값은 약분되어있지 않음.

 

 

💡 본 내용은 K.N.King의 C Programming: A Modern Approach 책을 참고하였습니다.

'📚 STUDY > 🔥 C' 카테고리의 다른 글

[4-1] 산술연산자  (1) 2024.09.03
Q&A  (3) 2024.09.02
[3-1] printf 함수  (4) 2024.09.02
Q&A (GCC, GNU, 함수, 주석, 식별자, 레이아웃..)  (4) 2024.09.02
[2-8] C 프로그램의 레이아웃  (0) 2024.09.02