C언어 자습/혼자공부하는 C언어 (저자 서현우)

gets 함수를 사용한 문자열 입력(gets, fgets)

막뇌 2023. 4. 20. 14:13

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 로 바꾼다는 것입니다.

문자열의 마지막은 항상 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';

이 공식과 완벽하게 동일하다는걸 알 수 있습니다.