모바일 오유 바로가기
http://m.todayhumor.co.kr
분류 게시판
베스트
  • 베스트오브베스트
  • 베스트
  • 오늘의베스트
  • 유머
  • 유머자료
  • 유머글
  • 이야기
  • 자유
  • 고민
  • 연애
  • 결혼생활
  • 좋은글
  • 자랑
  • 공포
  • 멘붕
  • 사이다
  • 군대
  • 밀리터리
  • 미스터리
  • 술한잔
  • 오늘있잖아요
  • 투표인증
  • 새해
  • 이슈
  • 시사
  • 시사아카이브
  • 사회면
  • 사건사고
  • 생활
  • 패션
  • 패션착샷
  • 아동패션착샷
  • 뷰티
  • 인테리어
  • DIY
  • 요리
  • 커피&차
  • 육아
  • 법률
  • 동물
  • 지식
  • 취업정보
  • 식물
  • 다이어트
  • 의료
  • 영어
  • 맛집
  • 추천사이트
  • 해외직구
  • 취미
  • 사진
  • 사진강좌
  • 카메라
  • 만화
  • 애니메이션
  • 포니
  • 자전거
  • 자동차
  • 여행
  • 바이크
  • 민물낚시
  • 바다낚시
  • 장난감
  • 그림판
  • 학술
  • 경제
  • 역사
  • 예술
  • 과학
  • 철학
  • 심리학
  • 방송연예
  • 연예
  • 음악
  • 음악찾기
  • 악기
  • 음향기기
  • 영화
  • 다큐멘터리
  • 국내드라마
  • 해외드라마
  • 예능
  • 팟케스트
  • 방송프로그램
  • 무한도전
  • 더지니어스
  • 개그콘서트
  • 런닝맨
  • 나가수
  • 디지털
  • 컴퓨터
  • 프로그래머
  • IT
  • 안티바이러스
  • 애플
  • 안드로이드
  • 스마트폰
  • 윈도우폰
  • 심비안
  • 스포츠
  • 스포츠
  • 축구
  • 야구
  • 농구
  • 바둑
  • 야구팀
  • 삼성
  • 두산
  • NC
  • 넥센
  • 한화
  • SK
  • 기아
  • 롯데
  • LG
  • KT
  • 메이저리그
  • 일본프로야구리그
  • 게임1
  • 플래시게임
  • 게임토론방
  • 엑스박스
  • 플레이스테이션
  • 닌텐도
  • 모바일게임
  • 게임2
  • 던전앤파이터
  • 마비노기
  • 마비노기영웅전
  • 하스스톤
  • 히어로즈오브더스톰
  • gta5
  • 디아블로
  • 디아블로2
  • 피파온라인2
  • 피파온라인3
  • 워크래프트
  • 월드오브워크래프트
  • 밀리언아서
  • 월드오브탱크
  • 블레이드앤소울
  • 검은사막
  • 스타크래프트
  • 스타크래프트2
  • 베틀필드3
  • 마인크래프트
  • 데이즈
  • 문명
  • 서든어택
  • 테라
  • 아이온
  • 심시티5
  • 프리스타일풋볼
  • 스페셜포스
  • 사이퍼즈
  • 도타2
  • 메이플스토리1
  • 메이플스토리2
  • 오버워치
  • 오버워치그룹모집
  • 포켓몬고
  • 파이널판타지14
  • 배틀그라운드
  • 기타
  • 종교
  • 단어장
  • 자료창고
  • 운영
  • 공지사항
  • 오유운영
  • 게시판신청
  • 보류
  • 임시게시판
  • 메르스
  • 세월호
  • 원전사고
  • 2016리오올림픽
  • 2018평창올림픽
  • 코로나19
  • 2020도쿄올림픽
  • 게시판찾기
  • 게시물ID : programmer_10129
    작성자 : kosi
    추천 : 3
    조회수 : 3986
    IP : 118.37.***.207
    댓글 : 9개
    등록시간 : 2015/05/16 14:48:59
    http://todayhumor.com/?programmer_10129 모바일
    scanf... 그것이 알고 싶다!
    쓰다보니 쓸 데 없이 길어졌네요..
    바쁘신 분은 세줄요약만 보셔도 됩니다....

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

    과제 때문인지 업무 때문인지는 모르겠지만,
    C언어의 scanf 함수 사용에 대한 질문 글이 꾸준히 올라오네요. ^^;

    사실, scanf 를 업무용 프로그램에서 쓰는 일은 '거의' 라고 쓰고 '네버'라고 읽음 없습니다.
    왜냐 하면, 여러 질문에 나와 있는 '그 문제점들' 때문이죠.. ㅋㅋ.
    그리고, scanf 함수의 실제 구현과 동작은 각 시스템(Unix/Linux/Windows...)별로 다르고,
    컴파일러별로 다릅니다. C 표준에 정의된 사양만 그것에 부합하도록 같을 것이고, undefined behavior는 제조사마다 다를 겁니다.

    아무튼, 저도 scanf 질문글이 올라올 때마다,

    "뭐, 또, 엔터키 입력된 것 버퍼에서 안지워서 그렇겠지..."

    요정도 생각하고 들어 옵니다. 그리고 실제로 예지력 상승을 확인하고 나가죠...

    그러다 문득, 정말로 scanf 가 어떤 짓을 하도록 표준에 정의되어 있는 걸까...
    왜 그따위로 만들어서 여러 착한 사람들을 못살게 굴까 궁금해지기 시작했습니다.

    그래서, 일단, 소스코드를 좀 봐야겠다... 싶어서 찾아 봤죠.
    물론 시스템별로 구현이 다르니, scanf 소스코드도 여러 버전이 있습니다.
    그리고 요기 http://mirror.fsf.org/pmon2000/3.x/src/lib/libc/scanf.c 에는 좀 간략한 버전이 있더군요.

    좀 읽어 보려다가... 금세... 아몰랑.. 시전..
    뭐 소스를 꼭 이해해야 하는 건 아니잖아, 어차피 시스템마다 구현도 다른 걸... 이라는 자기합리화... -_-;;

    뭐, 아무튼, scanf 를 쓰면서 오는 혼란 중 가장 큰 것은,
    scanf 가 input buffer 를 '비울 때도 있고 안비울 때도 있기' 때문이라고 봅니다.
    게다가, scanf 의 표준에는 scanf 가 동작하다가 예외를 만났을 때의 상황을 일일이 정의하지 않고 있기 때문에,
    Windows/Linux/Unix/Mac 등등에서 서로 다른 시스템과 컴파일러를 사용하면서
    서로 묻고 답하는 와중에, 또다시 혼란이 생기고... 하여... 문제가 더 복잡해 지지 않았나... 마.. 그런 생각이 듭니다.

    흔히 scanf 관련 질문 올라올 때는 아래와 같은 패턴이죠:

    scanf("%d", &var1);
    printf("%d\n", var1);

    scanf("%d", &var2);
    printf("%d\n", var2);

    저기서, 첫번째 scanf에 입력을 했는데 왜 두 번째 scanf가 실행이 안되고 쭉 지나가 버리는가...하는 것이 가장 많지요?
    거기에 대한 답은 여러분도 익히 아시다시피 첫번째 scanf 문에서 입력을 할 때,
    마지막에 enter 를 넣은 것이 input buffer에 남아 있어서 그렇다. 그러니 아래와 같이 고쳐라..는 겁니다.

    scanf("%d", &var1);
    printf("%d\n", var1);

    getchar();

    scanf("%d", &var2);
    printf("%d\n", var2);

    요렇게 하면 해결이 된다! 맞습니다. 하지만, 예외처리가 완전히 된 것은 아닙니다.
    만약, 첫 번째 scanf 함수에서 사용자가 숫자가 아닌 문자를 '여러 개' 입력하면,
    또는 숫자와 문자를 합쳐서 11aaaa 와 같이 입력하면 또 다른 예외상황이 발생하게 됩니다.

    그래서, 입력을 받고 나면 무조건 버퍼를 비워라!
    라는 뜻으로 아래와 같이 하는 방법도 제시합니다.

    scanf("%d", &var1);
    printf("%d\n", var1);

    scanf("%s", str);

    scanf("%d", &var2);
    printf("%d\n", var2);

    요렇게 하면, 입력 버퍼가 비워지는 것은 맞습니다. 대부분의 경우 동작도 잘 될겁니다.
    다만, 저기서 사용자가 아주 긴.. 문자열을 입력하게 되면, 즉, str 이라는 버퍼의 크기를
    넘는 문자열을 입력하게 되면, 또 다시 예외상황이 발생하게 됩니다.

    그래서 이번에는, scanf로 비우지 말고, 아래와 같이 비워라... 하는 방법도 있습니다.

    scanf("%d", &var1);
    printf("%d\n", var1);

    fflush(stdin);

    scanf("%d", &var2);
    printf("%d\n", var2);

    요렇게요. 근데, 문제가 있습니다.
    fflush 라는 함수는 output buffer 에 있는 것을 모두 해당 stream으로 보내버리라는 것이지 (변기에서 물 내리는 것 처럼요)
    input stream에 대해서 동작하도록 C 표준에 있지는 않은 것 같습니다. (참조: http://c-faq.com/stdio/stdinflush2.html)
    그런데, 더 큰 문제는, fflush(stdin) 이라는 함수가 어떤 시스템 환경(아마도 Windows)에서는 동작하기도 하는 모양입니다??? (참조: https://kldp.org/node/29034)

    그리고, scanf("%d", &var) 등으로 읽을 때,
    어떤 시스템에서는 숫자 입력하고 난 후 'enter'를 누른 것을 input buffer에서 없애버리고,
    어떤 시스템에서는 남겨두고.. 하니까 이게... 더 혼란이 오는 것 같습니다.
    그리고, 이건 순전히 제 추측인데요.
    Windows 와 Linux 에서 개행(New Line)을 처리하는 문자가 다릅니다. 아시죠?
    Windows : 0D0A (Carriage-Return + Line-Feed)
    Linux/Unix/Mac : 0A
    요렇게 다른데요, Windows(또는 DOS)에서의 scanf는 0D는 읽어서 없애지만,
    0A는 남겨두는 것 같습니다.
    그러니, linux등에서 잘 되던 소스가 window에서 실행하면 이상하게 안되는 현상이 발생하기도 할 것이고요...

    그런데, 실제로 VisualC/C++ 등에서 구현된 scanf 의 소스코드를 보지 않았기 때문에,
    0D만 읽어서 없애고, 뒤따라오는 0A는 내버려 두는 일을 실제로 하는 지는 확인되지 않았습니다.

    scanf에 대해서 또하나 아셔야 할 것은,
    input stream에서 format에 지정된 타입의 값을 읽지 못하면 input stream buffer를 비우지 않는다는 것인데요,
    사실 이렇게 말하는 것 보다는 아래와 같이 말하는 것이 더 정확할 겁니다.
    "Input Stream에서 format에 지정된 type의 값을 읽게 되면 해당 값을 읽을 때까지의 버퍼는 비운다" 가 맞을 겁니다.
    무슨 말인고 하면,

    scanf("%d", &var); 로 사용자 입력을 읽을 때, 사용자가 입력을:

    1) 1234
    2) abcd
    3) ab12
    4) 12ab

    의 네 가지 경우가 있다고 가정하면,
    ----var에 들어가는 값------버퍼에 남아있는 문자---
    1) ==> 1234 ________________(없음)
    2) ==> (없음)________________abcd
    3) ==> (없음)________________ab12
    4) ==> 12 ___________________ab
    와 같이 된다는 겁니다.
    위에서 %d 로(즉, 숫자) 읽으라고 했으니, scanf는 input buffer에서 숫자를 찾습니다.
    1)의 경우에는 숫자가 찾아졌으므로 읽어서 변수에 할당하죠, 그리고 읽어낸 값을 inuput buffer에서 지웁니다.
    2)의 경우에는 숫자가 없으므로 변수는 변동되지 않지만, abcd 라는 문자는 '여전히' input buffer에 남아 있습니다.
    3)의 경우에도 2)와 다를 바 없습니다.
    4)의 경우에는 먼저 들어 온 숫자 12를 읽고 그 다음 문자(ab)를 만나게 되어서 12만 값을 넣고, 버퍼에서 12를 지우고, ab는 남겨 놓습니다.

    자, 이제 거의 다 왔네요 ^^;
    scanf 의 함수 이름이, SCAN 이잖아요... getInput 이나 readInput 이 아니고...
    그 말은, 뭔가를 scan....한다는 뜻이겠죠? 즉, input buffer를 scan 한다는 겁니다.
    scanf는 input buffer의 내용을 scan하면서, 주어진 format에 지정된 data-type에 matching되는 값을 찾아서
    주어진 변수에 할당을 하는 일을 하는 것이지, 단순히 키보드를 입력 받아서 그것을 실시간으로 변환하는 것이 아니기 때문에,
    이름을 get...이나 read..가 아닌 scan으로 지은 것이 아닌가.. 합니다.

    scan.jpg

    그럼 어떻게 scanf를 써야 하는가...?

    우선, 입력을 받기 위해서 scanf를 쓸 때는,
    실제로 scanf가 입력을 제대로 받았는지 확인하는 것이 가장 기본일 것 같습니다.

    scanf는 int형의 return 값이 있는데, format에 지정된 대로 값을 읽은 결과, 성공적으로 읽어서 변수에 저장한 갯수를 리턴합니다.

    ret = scanf("%d%f%c", &v1, &v2, &v3);

    위의 경우, scanf를 통해서 성공적으로 읽어 들인 값이 1개인지, 2개 또는 3개인지에 따라,
    ret의 값이 정해지게 됩니다. 물론, 하나도 못읽어들였다면 ret의 값은 0이 되겠죠.
    따라서, scanf의 리턴값과 v1/v2/v3 등의 변수의 값을 조사해서,
    적절한 예외 처리와 조치를 취하는 것이 좋을 것 같습니다.

    그리고, 여러 번의 scanf 함수 호출로, 사용자로부터 입력을 받아야 할 경우에는,
    input buffer에 남아 있는 white space나 개행문자, 또는 필요없이 남아 있는 문자들을 잘 제거해 주는 것이 필요하고요,
    특히나, Windows/Dos 에서는 엔터키를 칠 때 0D0A 라는 두개의 캐릭터가 input stream buffer에 들어가기 때문에,
    0A가 남아서 다음번 scanf 입력에서 원치않는 결과를 초래하는 것을 잘 방지해야 한다고 봅니다.

    그래서,
    10개의 정수를 입력받는 프로그램을 scanf를 써서 구현할 때,
    가장 이식성이 좋게 만든다면,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #include <stdio.h>
     
    int main()
    {
        int i;
        int c;
     
        i = 0;
        do
        {
            printf("Input Number[%d]: ",i);
     
            switch(scanf("%d",&c))
            {
                case 1:
                    printf("Input: %d\n",c);
                    i++;
                    break;
                default:
                    printf("No Integer Found\n");
                    printf("Check Your Input: >>>");
                    while((c = getchar()) != '\n' && c != '\r' && c != EOF) printf("%c",c);
                    printf("<<<\n");
                    break;
            }
        }
        while(i<10);
     
        return 0;
    }
     
    cs

    위와 같이, scanf의 리턴값을 조사해서 값을 제대로 읽어들이지 못했을 경우에는 input buffer를 비우도록 하는 것이 좋을 것 같습니다.

    결론 (세줄 요약)

    1. scanf를 여러 번 호출 할때, input buffer를 잘 지우자! (특히 Windows에서 0A 값이 남아서 문제가 자주 된다!)
    2. scanf는 입력 버퍼를 'SCAN' 하고, 원하는 형식의 값이 있으면 버퍼를 비우지만, 아니면 버퍼를 비우지 않고 그대로 놔둔다!
    3. scanf도 리턴 값이 있다. 리턴값을 잘 활용하자!

    입니다... 그리고 참고로, fflush(stdin) 함수는 DOS에선 잘 동작하지만 유닉스 계열에선 동작하지 않는다는 것도요~

    마지막!
    scanf 함수는 실제 업무에서는 '전혀' 쓰이지 않습니다... 고로 지금까지 전 뻘짓한겁니다...ㅠㅠ
    kosi의 꼬릿말입니다
    You never know what you're gonna get

    이 게시물을 추천한 분들의 목록입니다.
    [1] 2015/05/16 15:04:36  220.87.***.93  피소금  81642
    [2] 2015/05/16 15:09:56  112.155.***.84  파송송드립탁  331483
    [3] 2015/05/17 08:11:40  221.156.***.183  뻐꾸기77  597022
    푸르딩딩:추천수 3이상 댓글은 배경색이 바뀝니다.
    (단,비공감수가 추천수의 1/3 초과시 해당없음)

    죄송합니다. 댓글 작성은 회원만 가능합니다.

    번호 제 목 이름 날짜 조회 추천
    23441
    예전에는 함수 하나에 대한 기능에 고민을 많이 했는데.. ssonacy 24/05/21 09:45 700 0
    23440
    c++ 에서 DB 쿼리문처럼 사용할 방법이 있을까요? [8] 상사꽃 24/05/19 11:10 821 0
    23439
    쉬운 배터리 알림 창작글 언젠가아자 24/05/14 10:47 1027 0
    23438
    아후 서터레스 NeoGenius 24/04/02 17:52 869 1
    23436
    로또 [3] 까망사투리 24/03/11 15:53 1388 4
    23434
    copilot 기업유료버전 intelliJ에 붙여서 쓰고있는데 지리네요 안녕월드 24/02/22 00:15 1437 0
    23433
    코딩마을 대나무숲 [7] cocoa 24/02/20 14:50 1576 5
    23432
    (질문) 프로그래머분들은 싱글PC게임 레벨제한 풀수 있죠?? [22] 본인삭제금지 할배궁디Lv2 24/02/13 13:36 1631 1
    23431
    Freemium NeoGenius 24/02/13 13:23 1161 0
    23429
    부산에서 프로그래머 구인하는데 연봉 6천에서 8천 작은건가 [3] 폴팡 24/02/04 20:50 1835 1
    23427
    chatgpt? bard? [4] 별빛러브 24/01/25 06:24 1292 0
    23426
    Next.js로 만들어봤어요~ [3] 창작글 sonnim 24/01/24 12:52 1448 3
    23425
    Spring Boot 공부하기 - 한국투자증권 오픈API 호출 옐로우황 24/01/21 17:51 1407 1
    23424
    파이썬 코딩 관련해서 질문드립니다. [4] 투투나 24/01/08 09:49 1595 0
    23423
    9년차 개발자의 "나만의 챗봇" 만들기 with ChatGPT [2] 아자뵤옹 23/12/10 22:35 1795 4
    23420
    이 에러가 뭘까요? [2] +.푸른바다.+ 23/11/03 15:25 1929 1
    23419
    [유니티 코리아] MWU 2023 투표하고 푸짐한 경품 받아가세요! engine1 23/10/06 18:52 1512 0
    23418
    Flutter로 만든 채팅 어플리케이션 with ChatGPT 아자뵤옹 23/09/13 22:39 2017 0
    23417
    특정 페이지 직접 접근 어떻게 막으시나요? [9] 달콤아시타 23/09/10 09:36 2041 0
    23416
    버츄얼 유튜버가 완성한 '세계 최초' 애플 실리콘 GPU 드라이버 펌글 우가가 23/09/02 23:52 2155 2
    23415
    뜨끈뜨끈한 30분짜리 삽질 [9] 창작글 상사꽃 23/08/29 16:00 2442 1
    23414
    [유니티 코리아] MWU 코리아 어워드 2023 마감 임박! mwuaward2023 23/08/26 14:01 1613 0
    23413
    [유니티 코리아] MWU 코리아 어워드 2023 mwuaward2023 23/08/13 19:52 1647 0
    23412
    React.js 공부하기 - REST API 호출(CRUD) 옐로우황 23/08/05 13:13 1900 0
    23411
    영어앱을 만들었는데, 사용자들의 의견 받고 싶습니다! [2] 맑은바다13 23/08/03 18:28 1865 2
    23410
    진짜 절박해서 정말 ㅠㅠ 첫끼간절해서 도움주실분ㅠ.. [3] 명금123 23/07/17 22:28 1954 0
    23409
    [유니티 코리아] MWU 코리아 어워드 2023 mwuaward2023 23/07/04 16:49 1755 0
    23407
    라즈베리파이 파이썬코드에 while문 썼는데 동작을 안해요 [3] 싱그러운햇살 23/06/17 17:18 2031 1
    23405
    라즈베리파이, 스위치 하나 누르면 다른 스위치들도 반응해요 [3] 싱그러운햇살 23/06/15 22:39 2081 1
    23403
    혹시.. 중소기업 재취업 목표.. 공부방법 및 툴 버전 질문드려도 될까요 [2] 베스트금지베오베금지외부펌금지 웃대메템 23/06/13 01:46 2184 0
    [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [다음10개▶]
    단축키 운영진에게 바란다(삭제요청/제안) 운영게 게시판신청 자료창고 보류 개인정보취급방침 청소년보호정책 모바일홈