gets 함수를 사용한 문자열 입력(gets, fgets)
gets 함수 뜯어보기
함수의 선언
char *gets(char *str)
gets 함수의 사용에 대해 예제로 알아보기
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
int main() {
char str[80];
printf("공백이 포함된 문자열 입력:");
gets(str); //배열명을 인수(argument) 로 넘기는 방법으로 함수를 호출한다.
printf("입력한 문자열은 %s 입니다.", str);
return 0;
}
개행문자가 확인 될 때까지 공백을 포함해서 계속 입력 받는것을 확인할 수 있었다.
중요한것은 gets 함수가 입력 버퍼에서 개행문자까지 가져오지만 배열에 저장할 때에는 개행문자를 NULL 로 바꾼다는 것입니다.
Tistory
좀 아는 블로거들의 유용한 이야기
www.tistory.com
주의해야 할 점은 배열의 크기보다 큰 문자열을 입력하면 메모리 공간을 벗어나기 때문에 에러가 발생한다는 점입니다.
Stack around the variable 'str' was corrupted << 이런 에러가 발생하네요.
fgets 함수 뜯어보기
fgets(char *_Buffer, int _MaxCount, FILE *_Stream)
char* _Buffer는 문자열 배열을 받겠다는 이야기 입니다.
int _MaxCount는 어떤 크기만큼 입력을 받을지를 결정하는데 보통 배열의 크기를 설정합니다. sizeof(arr)을 넣지요.
FILE* _Stream 자리에는 주로 stdin을 넣는데 어떤 파일 스트림을 사용할지를 결정합니다.
stdin은 데이터를 입력할 때 키보드와 연결된 표준입력 버퍼를 사용하라는 뜻입니다. 키보드로 입력할 때는 항상 stdin을 넣어야 합니다.
fgets함수의 사용에 대해 예제로 알아보기
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main() {
char str[80];
printf("공백이 포함된 문자열 입력:");
fgets(str, sizeof(str), stdin); //문자열 입력을 받는다.
//
printf("입력된 문자열은 %s입니다.\n", str);
return 0;
}
fgets로 받았을 때는 개행문자가 포함되어 입력되는것을 볼 수 있습니다. 그냥 gets를 쓸 때에는 개행문자를 NULL 로 변경 해 주었지만 fgets는 아니네요. 개행문자까지 배열에 포함되고서 그 다음에 마지막에 NULL값이 들어가는것을 알 수 있었습니다.
이럴때는 마지막의 개행문자를 제거하는 공식을 사용하면 됩니다.
str[strlen(str)-1] = '\0'
strlen은 문자열의 길이값을 반환합니다. 거기서 1을 뺀 거니까 배열의 index값으로 보면 최대 index값을 가리키게 되는거죠.
문자열 길이의 마지막 값을 NULL로 변환해줌으로서 개행문자를 NULL로 바꾸게 됩니다.
위에서 사용한 gets 함수의 동작과 동일하게 되는 거죠.
위 코드에서 // 이 있는 행을 공식으로 대체 한 모습입니다.
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main() {
char str[80];
printf("공백이 포함된 문자열 입력:");
fgets(str, sizeof(str), stdin); //문자열 입력을 받는다.
str[strlen(str) - 1] = '\0';
printf("입력된 문자열은 %s입니다.\n", str);
return 0;
}
출력값도 우리가 원하는 대로 출력 되었습니다.
또다른 차이점은 sizeof(str)로 배열의 크기만큼 입력받겠다는것을 명시해주었기 때문에 더 많은 값을 입력 하더라도 딱 그 크기만큼의 입력만 받고 나머지 값들은 버리게 됩니다. 초과된 값들을 안전하게 처리가 가능하죠.
표준입력함수의 버퍼 공유 문제
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main() {
int age;
char name[20];
printf("나이 입력: ");
scanf("%d", &age);
printf("이름 입력: ");
gets(name);
printf("나이 :%d, 이름 :%s\n", age, name);
printf("123");
fputs(name, stdout);
printf("456");
return 0;
}
같은 입력 버퍼를 공유하기 때문에 나타나는 문제입니다.(stdin)
첫번째 scanf("%d", &age)에 17를 입력하고 엔터를 쳤기 때문에 age에는 17이 잘 들어있습니다.
그런데 버퍼에 남은 개행 문자가 자동으로 gets(name)를 통해서 name에 들어가게 됩니다.
gets함수에서 name을 받고 보니 개행문자만 있는 거예요
gets는 개행문자를 만나면NULL로 바꾸고 저장하죠. 따라서 name의 배열에는 그냥 NULL만 남아 버렸다고 할 수 있습니다.
123과 456 사이의 fputs 출력에 아무것도 출력되지 않은걸 보면 알 수 있죠.
그래서 입력 값에서 개행문자가 불필요 한 경우 제거하는 공식이 필요합니다.
공식에는 3가지가 있습니다.
getchar();
scanf("%*c");
fgetc(stdin)
3가지 모두 입력 버퍼에 남은 문자를 제거하는 공식입니다. 한문자씩 제거가 되겠네요.
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main() {
int age;
char name[20];
printf("나이 입력: ");
scanf("%d", &age);
fgetc(stdin); //getchar(); scanf("%*c"); 모두 같은 역할을 함
printf("이름 입력: ");
gets(name);
printf("나이 :%d, 이름 :%s\n", age, name);
return 0;
}
이제 예상대로 출력이 됩니다.
직접 gets 함수 구현 해보기
gets 함수는 공백을 포함하여 문자열을 받고, 개행 문자를 NULL로 바꾸는 역할을 합니다.
이를 직접 구현해 보면 아래와 같습니다.
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
int main() {
int i = 0; //loop counter
char str[20];
char ch;
do
{
ch = getchar();
str[i] = ch; //str index = loop counter (0 ~ ch = '\n')
i++;
} while (ch != '\n');
str[--i] = '\0';
//printf("%s", str);
return 0;
}
str[--i] = '\0';
여기서 이 문장은 개행문자 제거 공식과 완벽하게 같은 역할을 합니다.
str[strlen(str)-1] = '\0';
이 공식과 완벽하게 동일하다는걸 알 수 있습니다.