[C] 잠시만 들렸다가 갈게요.

장세민·2022년 9월 18일

📝 TIL

목록 보기
17/40

📌 버퍼

'scanf'나 'getchar' 함수와 같은 표준 입력 함수들은 데이터를 입력할 때 버퍼를 거쳐 입력한다.

버퍼는 프로그램에서 직접 할당하는 것이 아니고
프로그램을 실행하는 중에 운영체제가 자동으로 할당하는 메모리의 저장 공간이다.

쉽게 말해서 키보드로 입력하는 데이터는
일단 버퍼라는 공간에 저장된 후 scanf 함수에 의해 변수에 입력된다.


📖 scanf 함수가 문자를 입력하는 과정

버퍼는 데이터를 보관하는 역할이므로
필요한 데이터를 한꺼번에 저장해놓으면 scanf 함수는 호출 즉시 버퍼에서 데이터를 가져올 수 있다.

# include <stdio.h>
 
int main(void)
{
	char ch;
	int i;
 
	printf("문자를 입력하세요.");
 
	for (i=0; i<3; i++)
	{
		scanf("%c", &ch);
		printf("%c", ch);
	}
 
	return 0;

문자를 입력하고 화면에 출력하는 과정을 세 번 반복한다.

최초 scanf 함수가 호출될 때 키보드로 문자열을 입력하면
일단 버퍼에 저장한 후에 첫 번째 문자만 변수 'ch'에 저장한다.

ex)
'tiger'를 입력하면 버퍼에 'tiger'를 저장하고, 첫 번째 문자 't'만 변수 'ch'에 저장!

두 번째 scanf 함수 호출부터는 버퍼에 남아있는 문자열에서 차례로 다음 문자를 가져오면 되므로
새로운 키보드 입력이 필요 없다.

만약 scanf 함수가 버퍼에 저장된 데이터를 다 가져오면 추가 입력을 해야겠지?


여기서 잠깐

입력 데이터는 Enter를 누르는 순간 버퍼에 저장되며 개행 문자도 함께 저장된다.
따라서 버퍼에 있는 개행 문자도 하나의 데이터로 입력될 수 있고,

while (1)
{
	scanf("%c", &ch);
	if (ch == '\n') break;
	printf("%c", ch);
}

이렇게 개행 문자가 나올 때까지 문자를 반복적으로 입력하여 출력할 수 있다.

🚨 입출력 함수가 버퍼를 왜 쓸까?

1. 데이터를 안정적으로 입력받을 수 있다.

버퍼는 일정 크기의 연속된 저장공간이므로,
프로그램이 입력을 즉시 받지 못해도 데이터가 사라지지 않아 안정적이다.

2. 둘째, 입력장치와 독립적으로 사용할 수 있다.

scanf 함수는 키보드와 직접 연결되는 것이 아닌 버퍼에서 입력을 받는 것이다.
따라서 입력장치가 바뀌더라도 함수를 수정하지 않고 사용할 수 있다.


📖 scanf 함수 반환값 활용

키보드로 한 줄을 입력할 때 입력을 끝내려면 Enter를 누르면 되지만,
개행 문자를 입력 데이터로 쓴다면 입력을 종료하는 별도의 신호가 필요할 것이다.

scanf 함수는 키보드로 Ctrl + Z를 누르면 -1를 반환한다.

유닉스나 리눅스 시스템에서는 Ctrl + D

# include <stdio.h>
 
int main(void)
{
	int res;
	char ch;
 
	printf("문자를 입력하세요.");
 
	while (1)
	{
		res = scanf("%c", &ch);
		if (res == -1) break;
		printf("%d ", ch);
	}
 
	return 0;
 
}

실행결과는 입력과 출력이 Enter를 기준으로 반복된다.
첫 번째 행의 입력은 두 번째 행에서 아스키 코드 값을 출력하고 새로운 입력을 기다린다.

입력한 데이터 'cat'은 Enter를 누른 순간 버퍼로 저장되고 반복문이 수행되면서
버퍼의 문자를 하나씩 가져다 아스키 코드 값을 출력한다.

마지막으로 Ctrl + Z를 누르면 -1를 반환하고, if문 조건식이 참이되어 반복 종료!

++ scanf 함수의 반환값과 비교하는 값 EOF

scanf 함수의 반환값과 비교하는 값으로 -1대신 EOF를 쓸 수 있다.
End Of File의 뜻으로 -1 대신 입력의 끝을 의미하는 이름으로 사용할 수 있다는 거지.


📖 getchar 함수를 사용한 문자열 입력

  1. # include <stdio.h>
  2.  
  3. void my_gets(char *str, int size);
  4.  
  5. int main(void)
  6. {
  7. char str[7];
  8.  
  9. my_gets(str, sizeof(str));
  10. printf("입력한 문자열: %s\n", str);
  11.  
  12. return 0;
  13. }
  14.  
  15. void my_gets(char *str, int size)
  16. {
  17. int ch;
  18. int i = 0;
  19.  
  20. ch = getchar();
  21. while ((ch != '\n') && (i < size -1))
  22. {
  23. str[i] = ch;
  24. i++;
  25. ch = getchar();
  26. }
  27. str[i] = '\0';
  28. }

getchar 함수를 사용하여 한 줄의 문자열을 char 배열로 저장한다.
단, 배열의 크기를 넘는 문자열을 입력한 경우배열의 크기만큼만 입력된다.

함수 안에서는 20행에서 최초로 문자를 입력하는데 이때 데이터를 모두 입력하여 버퍼에 저장해둔다.
이후 getchar 함수를 반복 사용하여 버퍼로부터 문자를 하나씩 가져와 배열에 차례로 저장한다.


📖 입력 버퍼 지우기

scanf와 getchar 함수는 같은 버퍼를 사용하여 입력 데이터를 공유한다.
앞서 실행한 입력함수가 버퍼에 남겨둔 데이터를 그 이후에 수행되는 함수가 가져가면?

대참사

따라서 버퍼에 남아 있는, 즉 그 이후에 수행되는 함수와 관련이 없는 불필요한 데이터는 미리 제거해야 한다.

입력 버퍼의 내용을 지우기 위해서는 버퍼에 남아 있는 문자들을 모두 입력해서 사용하지 않고 버리면 된다!

  1. # include <stdio.h>
  2.  
  3. int main(void)
  4. {
  5. int num, grade;
  6.  
  7. printf("학번 입력: ");
  8. scanf("%d", &num);
  9. getchar();
  10. printf("학점 입력: ");
  11. grade = getchar();
  12. printf("학번: %d, 학점: %c", num, grade);
  13.  
  14. return 0;
  15. }

8행에서 '315'를 입력하고 Enter를 누르면 '315'와 개행문자가 함께 버퍼에 저장된다.
그 후 문자열 "315"는 정수로 변환되어 변수 'num'에 저장되고 버퍼에는 개행문자만 남는다.

이 개행문자가 이후의 입력에 영향을 줄 수 있으므로
9행과 같이 getchar 함수를 추가로 호출하여 버퍼에 남아 있던 개행 문자를 제거한다.

++ 만약 개행 문자를 제거하지 않으면?

grade = getchar();
printf("학번: %d, 학점: %c", num, grade);


버퍼에 있는 개행문자를 getchar 함수가 가져가서
학점을 추가로 입력받지 못하고 프로그램이 계속 진행되며
grade에 저장된 개행문자는 12행에서 출력되어 줄이 바뀌게 된다.


📢 stdin과 stdout

문자 입출력 함수에는 'fgetc'와 'fputc'도 있다.
이들은 호출할 때 추가적인 인수가 필요하다.
별도의 인수를 주는 것 외에는 getchar, putchar 함수와 기능은 같다.

구분함수 사용법기능
int ch;int형 변수에 입력
입력ch = fgetc(stdin);공백 문자, 탭 문자, 개행 문자도 입력
입력 문자의 아스키 코드 값 반환
입력 버퍼 stdin 사용
------------------------------
출력fputc(ch, stdout);문자 출력 전용 함수
출력할 문자와 출력 버퍼 stdout 사용
profile
분석하는 남자 💻

0개의 댓글