훈, IT 공부/C,C++,MFC

C언어 입출력 함수 파헤치기 ( gets,getchar,puts,putchar, scanf )

IT훈이 2017. 12. 20.
반응형

C언어 입출력 함수 파헤치기


입출력이 너무나도 헷갈린다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
int main(void){

    int number = 0;
    char cData = 'a';
    char sData[20= { 0 };
 
    printf("숫자를 입력하세요:");
    scanf("%d"&number);
 
    printf("문자를 입력하세요:");
    scanf("%c"&cData);
 
    printf("문자열을 입력하세요:");
    scanf("%s", sData); //배열은 주소이기에 '&'를 사용하지 않는다.
 
    printf("숫자 : %d\n문자 : %c\n문자열 : %s\n", number, cData, sData);
 
    return 0;
//cs

결과값 
[Console]

숫자를 입력하세요:10
문자를 입력하세요:문자열을 입력하세요:Hello
숫자 : 10
문자 :

문자열 : Hello
계속하시려면 아무키나 누르십시오 . . .


결과값은 위와 같이 나오게 된다.


왜 문자를 입력받지 않을까???? 


왜 문자를 그냥 무시하고 넘어가는 것일까? 

궁금증이 생긴다. 


정상적인 결과이다.


이유는 우리가 입력하는 문자열 혹은 정수형태나 

마지막에는 "\0" , "NULL"값이 들어가게 되기 때문입니다.


의심이 들면 디버거로 한번 자세히 살펴보겠습니다. 


일단 상단에 입력 버퍼를 확인하기 위해

FILE *fp = stdin; 을 추가하였습니다.


디버거 모드 진입




1. 정수 10을 입력합니다. 

number메모리에는 정수 10 이 16진수 형태 0x0a로 들어갑니다.



2. 문자를 입력하기위해 진입합니다.



3. 문자를 입력하려 하지만?? 다음 printf() 함수로 넘어가 버렸습니다.

의문이 생깁니다



4. 문자는 무시하고 문자열을 받습니다. 


이쯤 종료하여서 3번에서의 원인을 파악하겠습니다.



그 전에 scanf의 동작 원리를 한번 설명하고 넘어가겠습니다..


1. scanf 는 사용자로 부터 형식 문자의 값을 받습니다.

2. 받은 값은 버퍼에 들어가고 버퍼의 값을 읽어서 변수의 주소에 저장합니다.


모든 형태문자를 첫 값을 자신이 받아들일 수 있는 값을 받아들인다. 


%d 와 %s같은 형태문자들은 '\n' 이러한 이스케이프 문자를 보고 그냥 넘어간다

그러나 %c 형태문자의 경우에는 ASCII 표에 있는 \n 개행 문자를 읽어들인다.




2번의 경우와 3번의 경우를 비교해보아야 합니다


2번의 경우

1. scanf("%d",&number); 함수는 사용자 입력값을 버퍼에 저장합니다. 

2. 저장한 버퍼의 값을 number주소에 값을 저장합니다. 

3. 이때 파일의 입력 부분을 보게되면 사용자 입력값은 카운트 되어 넘어가고 

_ptr 가르키는 값이 \n 개행문자라는 것을 볼 수 있습니다.


3번의 경우 

1. 현재 버퍼에 값 '\n' 를 가르치며 버퍼 호출 함수를 사용하면 '\n' 을 받아 갑니다.

2. scanf("%c",cData); 문자를 하나 받겠다 하는데, '%c' 형태문자는 이스케이프 문자를 읽어들입니다. 

'\n' 문자를 1byte 읽어 냅니다.

3. 읽어낸 결과는 바로 printf 함수에 찍어서 보여줍니다.


이 과정 때문에 우리가 입력하려하였던 문자를 입력하지 못하고 그냥 넘어갔던 것입니다.




해결방안


첫번째 

1
2
3
4
5
6
    printf("숫자를 입력하세요:");
    scanf("%d"&number);
    
    getchar();
    printf("문자를 입력하세요:");
    scanf("%c"&cData);





cs


개행 문자를 getchar() 함수를 이용하여 넘겨 버립니다.'


getchar() 함수의 특성은 버퍼에 값이 있으면 한 글자를 읽어 오는 것입니다.


이 특성을 이용하여서 야매;..? 로 개행 문자를 받아 넘어가는 방식입니다.


성공였다면 이러한 결과값이 반환 되었을 것입니다.




두 번째


fflush(stdin); 함수를 이용해 버퍼를 비웁니다.



1
2
3
4
5
6
    printf("숫자를 입력하세요:");
    scanf("%d"&number);
    
    fflush(stdin);
    printf("문자를 입력하세요:");
    scanf("%c"&cData);
cs

코드에 fflush(stdin) 함수를 추가하여 코드를 실행하면 첫 번째와 동일하게 실행된다.

하지만 이 방법은 windows계열에서만 사용가능하다는 단점이 있다.

리눅스에서는 fpurge()함수를 사용합니다.


결과값은 동일하게 나타날 것입니다.



세 번째


%d%*c 형태문자 %*c를 이용합니다.



1
2
3
4
5
    printf("숫자를 입력하세요:");
    scanf("%d%*c"&number);
 
    printf("문자를 입력하세요:");
    scanf("%c"&cData);
cs

scanf() 함수를 호출할 때, 형태문자에 %*c를 붙여주면 입력문자를 버퍼에서 하나 읽어와서 버린다는 뜻이 됩니다.

'*' 이 버려준다는 뜻으로 해석됩니다. 

다양한 환경에서 사용하려면 형식문자를 사용하는 것이 바람직합니다.

반응형

댓글