C언어는 기본적으로 자주 사용되는 함수들을 미리 정의해 놓았다.
표준 입력 함수들 역시 미리 정의되어 있는 함수들 중 일부이다.
여기서 알아볼 함수는 scanf(), gets(), getchar(), sscanf() 이다. (모두 stdio.h 에 선언되어 있다.)
gets() 와 getchar() 는 scanf() 로서 모두 처리해 줄 수 있다. 하지만 gets() 나 getchar() 를 사용하는 경우도 있다.
그 이유는 scanf() 보다 gets() 나 getchar() 가 안정적인 입력이 가능하기 때문이다. scanf() 는 상당히 불안정한 함수이다.
sscanf() 는 알아두면 상당히 유용한 함수이다. 문자열에서 원하는 자료형으로 추출이 가능한 엄청난 함수이다.
[[ char *gets( char * ) ]]
gets()는 문자열을 키보드로부터 입력받는 함수이다. <Enter>의 입력 전까지의 모든 문자를 입력으로 받아들인다.
입력 문자의 최대치는 통상적으로 256이다. 이건 컴파일러마다 다를 수 있으므로 참고적으로만 알아두기 바란다.
gets() 의 특징은 라인 입력이라는 점이고, 마지막에 눌러지는 <Enter>는 입력으로 받아들이지 않고 무시한다.
[ 예제 1 ]
#include <stdio.h>
void main()
{
char str[256];
gets ( str );
puts ( str );
}
[ 예제 1 의 실행 결과 ]
ABCDE<Enter> // 입력부 : gets() // 사용자가 입력하는 부분
ABCDE // 출력부 : puts() // 입력된 내용이 출력된다.
ABCDE<Enter>를 입력하게 되면 str[0] = 'A'; str[1] = 'B'; str[2] = 'C'; str[3] = 'D'; str[4] = 'E'; str[5] = 0; 이 입력된다.
[[ int getchar() ]]
getchar()는 키보드 버퍼에서 한개의 문자를 가져온다. 키보드 버퍼가 비어있다면 사용자의 키보드 입력을 기다린다.
이때는 1개의 문자를 입력한다고 해서 getchar() 함수가 종료되지 않으며, <Enter> 입력이 있을때까지 키보드 입력을 받는다.
리턴값이 int 이지만 통상적으로 char 범위내에서 리턴이 이뤄진다. 값을 받을 변수는 int 여도 되고 char 여도 상관없다.
[ 예제 2-1 ]
#include <stdio.h>
void main()
{
char ch;
ch = getchar();
putchar ( ch );
ch = getchar();
putchar ( ch );
}
[ 예제 2-1 의 실행 결과 #1 ]
abc<Enter> // 입력부 // <Enter>가 입력되어야 getchar()문을 탈출할 수 있다.
ab // 출력부 // putchar() 에 의해 2개의 문자가 출력된다.
첫번째 getchar() 에서는 키보드 버퍼가 비어있으므로 키보드 입력 대기상태가 되어 사용자의 입력을 기다리게 된다.
이때 abc<Enter> 가 입력되면 키보드 버퍼에는 a, b, c, 엔터 까지 4개의 문자가 저장되고 첫번째 getchar() 는 첫번째 a 를 가져온다.
두번째 getchar() 에서는 이미 키보드 버퍼에 b, c, 엔터 3개의 문자가 저장되어 있으므로 입력 대기 없이 가장 앞의 b 를 가져온다.
입력된 문자는 엔터를 포함해 4개인데 getchar()로 2개의 문자만을 가져왔으므로 키보드 버퍼에는 c 와 엔터가 남은 상태로 종료된다.
이때 뒤에 scanf() 나 gets() 가 있다면 c 와 엔터가 그곳의 입력으로 받아들여지는 불상사가 발생하게 된다. 이게 바로 버그가 된다.
뭐 이게 항상 버그는 아니다. 프로그래머의 의도가 그런 것일수도 있기 때문이다. 하지만 대부분은 버그가 되고, 보통 찾기도 어렵다.
getchar()는 동작 형태를 정확히 알고 사용하는 것이 중요하다. 이같은 점을 잘 알지 못한다면 예기치 못한 버그 때문에 고생하게 된다.
[ 예제 2-1 의 실행 결과 #2 ]
a<Enter> // 입력부 // <Enter>가 입력되어야 getchar()문을 탈출할 수 있다.
a<Enter> // 출력부 // getchar()는 gets()와 다르게 엔터도 입력으로 받아들인다.
[ 예제 2-2 ]
#include <stdio.h>
void main()
{
char ch;
ch = getchar();
fflush( stdin );
printf ( "%c\n", ch );
ch = getchar();
fflush( stdin );
printf ( "%c\n", ch );
}
[ 예제 2-2 의 실행 결과 ]
abc<Enter> // 입력부
a // 출력부
defg<Enter> // 입력부
d // 출력부
fflush( stdin ); 은 stdin 을 비워주는 작업을 한다. stdin 은 표준 입력 장치를 가르키는데 통상적으로 키보드 버퍼를 가르킨다.
즉, 첫번째 getchar() 에서 a, b, c, 엔터 를 입력받아 a 를 가져오고, 남은 b, c, 엔터 는 fflush() 에 의해 제거되는 것이다.
그러므로 두번째 getchar() 에서 키보드 버퍼가 비워져있으므로 또 입력 대기 상태가 되어 입력을 다시 받게 되는 것이다.
[[ int scanf( const char *, ... ) ]]
리턴값은 정상적으로 입력이 진행된 포멧의 갯수이다. 사실 잘 모르기 때문에 거의 사용되지는 않는데 리턴값을 잘 이용하면
입력 오류에 대한 예외 처리를 모두 수행할 수 있다. 수치 입력에 대해서 문자를 입력했을 경우 등을 체크할 수 있는 것이다.
scanf() 는 실질적으로 잘못된 입력에 대한 기본적인 예외처리가 불가능한 함수이다.
프로그래머도 잘 짜야하지만 사용자 역시 입력 형식에 맞게 제대로 입력을 주어야만 원하는 실행이 가능한 것이 scanf() 이다.
scanf() 는 printf() 와 사용법이 비슷한데.. 한가지 다른 점은.. scanf() 는 함수 내에서 주어진 변수의 값을 변경하여야 하므로..
인수는 모두 주소값으로 전달되어야한다.
설명보다는 그냥 예제로..
[ 예제 3-1 ]
#include <stdio.h>
void main()
{
char a, b[200];
int c; float d;
int i, j, k, l;
i = scanf ( "%c", &a );
j = scanf ( "%s", &b[0] ); // b 자체가 주소이므로.. scanf ( "%s", b ); 로 사용해도 무방하다.
k = scanf ( "%d", &c );
l = scanf ( "%f", &d );
printf ( "%c %s %d %f\n", a, b, c, d );
printf ( "%d %d %d %d\n", i, j, k, l );
}
[[[[ 주의할 점 ]]]]
b 는 배열이기 때문에 &b == b == &b[0] 이다. 따라서 scanf( "%s", &b ); 로 해도 동일한 결과를 얻을 수 있다.
하지만 b 가 포인터일 경우에는 달라진다. char s[200]; char *b = str; 일때.. &b != b.. b == &b[0] 가 되므로 주의해야한다.
위 [예제 3-1]을 제대로 수행하기 위해서는 입력을 정확하게 해 주어야한다.
[ 예제 3-1 의 실행 결과 #1 ]
A<Enter> // 입력부
ABC<Enter> // 입력부
123<Enter> // 입력부
1.5<Enter> // 입력부
A ABC 123 1.500000 // 출력부
1 1 1 1 // 출력부
여기서 중요한 점은 이렇게 정상적으로 입력하였어도 키보드 버퍼에는 엔터 문자(0x0A) 1개가 남아있게 된다.
확인하는 방법은 [예제 3-2] 를 실행해보면 된다.
[ 예제 3-1 의 실행 결과 #2 ]
AB<Enter> // 입력부
ABC<Enter> // 입력부
A B -858993460 -107374176.000000 // 출력부 // 입력이 잘못되면 완전히 이상한 결과가 초래된다.
1 1 0 0 // 출력부 // c 와 d 의 입력이 정상적이지 않음을 알 수 있다.
[ 예제 3-2 ]
#include <stdio.h>
void main()
{
char a, b[200];
int c; float d;
int i, j, k, l;
i = scanf ( "%c", &a );
j = scanf ( "%s", &b[0] );
k = scanf ( "%d", &c );
l = scanf ( "%f", &d );
printf ( "%c %s %d %f\n", a, b, c, d );
printf ( "%d %d %d %d\n", i, j, k, l );
c = getchar();
printf ( "%d\n", c );
}
[ 예제 3-2 의 실행 결과 ]
A<Enter> // 입력부
ABC<Enter> // 입력부
123<Enter> // 입력부
1.5<Enter> // 입력부
A ABC 123 1.500000 // 출력부
1 1 1 1 // 출력부
10 // 출력부 // getchar() 에 의해 사용자 입력을 대기해야 하지만 버퍼에 0x0A 가 있기때문에 그냥 넘어간다.
[ 예제 3-3 ]
#include <stdio.h>
void main()
{
char a, b[200];
int c; float d;
scanf ( "%c", &a ); fflush(stdin);
scanf ( "%s", &b[0] ); fflush(stdin);
scanf ( "%d", &c ); fflush(stdin);
scanf ( "%f", &d ); fflush(stdin);
printf ( "%c %s %d %f\n", a, b, c, d );
}
[[ int sscanf( const char *, const char *, ... ) ]]
리턴값은 정상적으로 입력이 진행된 포멧의 갯수이다. 입력 오류나 문자수 부족 등 예외 처리를 위해서는 꼭 필요한 값이다.
scanf() 의 기능과 비슷한데.. 다만 입력을 키보드가 아닌 char * 에서 받아온다는게 다른점이다. 그냥 예제로 보도록 하자.
[ 예제 4-1 ]
#include <stdio.h>
void main()
{
char a, b[200];
int c; float d;
int i;
i = sscanf ( "A ABC 123 1.5", "%c %s %d %f", &a, &b[0], &c, &d );
printf ( "%d %c %s %d %f\n", i, a, b, c, d );
}
[ 예제 4-1 의 실행 결과 ]
4 A ABC 123 1.500000
[ 예제 4-2 ]
#include <stdio.h>
void main()
{
char a, b[200];
int c; float d;
int i;
i = sscanf ( "AB ABC 123 1.5", "%c %s %d %f", &a, &b[0], &c, &d );
printf ( "%d %c %s %d %f\n", i, a, b, c, d );
}
[ 예제 4-2 의 실행 결과 ]
2 A B -858993460 -107374176.000000
sscanf() 도 scanf() 와 동일하게 입력 오류를 제대로 걸러내지 못하므로 항상 주의가 필요하다.
sscanf() 의 용도는 여러가지가 있는데 사용자 입력이 있는 프로그램을 테스트할때 계속해서 같은 입력을 주어야하는 경우 유용하다.
'C프로그래밍이론' 카테고리의 다른 글
2013.09.30 [C 언어 _ 스트림] (0) | 2013.09.30 |
---|---|
2013.03.26_컴파일시깨알팁 (0) | 2013.03.26 |
2013.03.26_헤더파일쓰는이유 (0) | 2013.03.26 |
2013.03.26_제일위에 입력되지 않은 함수가 쓰일수있는 이유(ex: printf() ) (0) | 2013.03.26 |
C교재 (0) | 2013.03.12 |