표준 입출력과 버퍼

ROK·2022년 7월 21일
0

앞에 표준 입출력에 대해 포스팅을 했다.
표준 입출력 함수에는 익히 알고있는 printf, scanf가 있고, fputc, fgetc도 있다.

표준 입출력 함수를 통해 데이터를 입출력 할 때, 그 데이터들은 운영체제(OS)가 제공하는 모리 버퍼를 중간에 통과하게 된다.

메모리 버퍼란?

데이터를 임시로 모아두는 메모리 공간으로, 표준 입력 함수는 입력 버퍼, 표준 출력 함수는 출력 버퍼를 거치게 된다.

작동 순서를 살펴보면 키보드를 통해 데이터가 입력된다고 하면,

  • 키보드 데이터 입력
  • 입력 버퍼에 저장(버퍼링)
  • 프로그램에 전달

위 순서를 거쳐 데이터가 입력된다.

여기서 잠깐, 키보드로 입력된 데이터가 입력 스트림을 거쳐 입력 버퍼로 들어가는 시점은 엔터키가 눌려지는 시점이다.

버퍼링(Buffering)을 하는 이유

왜 굳이 바로 데이터를 프로그램에 전달하지 않고 메모리 버퍼에 임시저장 하는 이유가 무엇일까??

그 이유는 데이터 전송의 효율성이다.
콘솔과 같은 외부장치와의 데이터 입출력은 생각보다 시간이 많이 든다. 그렇기 때문에 최대한 효율적으로 데이터를 전송하기 위해 사용한다.

예를 들어 짐을 옮긴다고 가정했을 때, 물건을 한 개씩 옮기는 것보다 수레에 짐을 담았다가 한번에 나르는 것이 효율적이다.

위 예시와 같은 효율을 위해 메모리 버퍼를 사용하는 것이다
입력된 데이터를 하나씩 전달하는 것은 비효율적이고, 메모리 버퍼에 데이터를 임시 저장했다가 한번에 전달하는 것이 훨씬 효율적이다.

그럼 이제 버퍼와 관련된 함수를 보자

출력버퍼를 비우는 fflush 함수

출력버퍼를 비운다는 것은 출력버퍼에 저장된 데이터가 버퍼를 떠나서 목적지로 이동됨을 의미한다.

#include <stdio.h>

int fflush(FILE * stream);
// 함수호출 성공 시 O, 실패 시 EOF 반환

fflush(stdout);와 같이 호출하면 어떤 시스템의 어떤 표준 출력버퍼든 저장된 내용이 비워지며 데이터가 목적지로 이동한다.

파일을 대상으로도 호출이 가능하다.
파일의 스트림 정보를 전달하면, 버퍼에 저장되어 있던 데이터들이 버퍼를 비우고 파일에 기록된다.

입력버퍼 비우기

입력버퍼의 비우기는 출력버퍼의 비우기와는 개념적으로 차이가 있다.

입력버퍼의 비우기는 데이터의 소멸을 의미한다.

입력버퍼의 경우 단순하게 함수로 정의도어 있지 않고 조금 어렵다.

예제와 함께 보자

#include <stdio.h>

int main() {
	char perID[7];
    char name[10];
    
    fputs("주민번호 앞 6자리 입력 : ", stdout);
    fgets(perID, sizeof(perID), stdin);
    
    fputs("이름 입력 : ", stdout);
    fgets(name, sizeof(name), stdin);
    
    printf("주민번호 : %s \n", perID);
    printf("이름 : %s \n", name);
    
    return 0;
}

perID[7]로 배열의 길이를 7로 선언했다
이는 주민번호 앞 6자리만 저장하기 위해서 널 문자까지 계산해 선언한 배열이다.

결과

주민번호 앞 6자리 입력 : 112233
이름 입력 : 주민번호 : 112233
이름 :

결과값이 이상하게 출력되었다. 6자리만 입력했는데 이름 입력을 받지않고 실행이 종료되어버렸다.

이런 결과값이 나온 이유는 입력 데이터에 있다.
우리가 입력한 값은 112233↵이다

6자리만 입력한 것처럼 보이지만 실제로는 엔터 키를 포함한 7자리를 입력한 되었다.

fgets함수는 \n을 만날때까지 읽어들이는 함수

따라서 첫 번째 fgets함수에서 널 문자를 제외한 6문자를 읽어들이고 \n은 입력버퍼에 남아있게 된다.
그리고 두 번째 fgets함수에서 입력버퍼에 남아있던 \n을 읽어 위와 같은 결과값이 나오게 된다.

그럼 \n을 지워주면 된다.

아래와 같은 함수를 사용한다.

void ClearLineFromReadBuffer(void)
{
	while(getchar() != '\n');
}

입력버퍼에 저장된 문자들은 읽어 들이면 지워진다.
따라서 위 함수는 \n을 만날때 까지 문자를 읽어들이는 함수이다.

그럼 최종적인 코드는 다음과 같다

#include <stdio.h>

void ClearLineFromReadBuffer(void)
{
	while(getchar() != '\n');
}

int main() {
	char perID[7];
    char name[10];
    
    fputs("주민번호 앞 6자리 입력 : ", stdout);
    fgets(perID, sizeof(perID), stdin);
    
    fputs("이름 입력 : ", stdout);
    fgets(name, sizeof(name), stdin);
    
    printf("주민번호 : %s \n", perID);
    printf("이름 : %s \n", name);
    
    return 0;
}

실행결과를 보면

주민번호 앞 6자리 입력 : 112233
이름 입력 : 홍길동
주민번호 : 112233
이름 : 홍길동

입력 버퍼에 남은 \n을 지워버렸기 때문에 정상적으로 입출력이 이루어진 것을 볼 수 있다.

만약 주민번호를 모두 입력한다고 해도 필요한 만큼만 읽어들이고 나머지는 지우기 때문에 정상적으로 작동한다.

주민번호 앞 6자리 입력 : 112233-1234567
이름 입력 : 김철수
주민번호 : 112233
이름 : 김철수
profile
하루에 집중하자

0개의 댓글