Skip to content

87. 입출력 버퍼


1. 키워드

  • 입출력 버퍼
  • 버퍼링(Buffering)


2. 입출력 버퍼 활용하기

  • C의 입출력 함수들은 내부적으로 입출력 버퍼를 사용하여 데이터를 처리한다.
  • 입출력 버퍼는 겉으로 드러나지 않으며 다음과 같이 함수를 사용할 때 선언하는 변수 buffer는 입출력 버퍼가 아니다.
  • 프로그래머 입장에서 데이터를 임시로 저장한다고 해서 버퍼라고 이름을 지은 것이다.


char buffer[100];
gets_s(buffer, sizeof(buffer));


  • 표준 입력(키보드)은 입력되는 문자를 입력 버퍼에 저장했다가 엔터 키(\n)가 입력되면 지정된 변수(배열, 할당한 메모리)로 옮긴다.
  • 마찬가지로 표준 출력(화면)은 출력 버퍼에 문자가 저장되었다가 특정 조건에 의해 버퍼가 비워지면 화면에 출력된다.
  • 입출력 버퍼가 비워지는 시점은 OS나 설정에 따라 달라진다.
  • 즉, 프로그램과 OS는 데이터를 효율적으로 처리하기 위해 입출력 버퍼를 사용한다.
  • 만약 입출력 버퍼 없이 데이터를 1바이트씩 처리하면 입출력이 될 때마다 매번 데이터를 처리해야 되므로 CPU 사용 횟수와 메모리 접근 횟수도 많아진다.
  • 하지만 입출력 버퍼를 사용하면 일정 크기만큼 데이터를 모아두었다가 처리하므로 그만큼 CPU를 덜 사용하고 메모리 접근 횟수도 줄어든다.
  • 이처럼 입출력 버퍼에 데이터를 저장하는 행동을 버퍼링이라고 부른다.


  • 이제 입출력 버퍼가 동작하는 모양을 살펴보자.
  • 다음은 출력 버퍼의 크기를 설정한 뒤 printf로 문자열을 출력한다.


setvbuf(파일포인터, 사용자지정입출력버퍼, 모드, 크기);
설정 변경에 성공하면 0 반환, 실패하면 0 아닌 값을 반환
#include <stdio.h>
#include <time.h>

void delay(unsigned int sec) // 특정 시간(초)만큼 기다리는 함수
{
    clock_t ticks1 = clock();
    clock_t ticks2 = ticks1;
    while ((ticks2 / CLOCKS_PER_SEC - ticks1 / CLOCKS_PER_SEC) < (clock_t)sec)
        ticks2 = clock();
}

int main()
{
    setvbuf(stdout, NULL, _IOFBF, 10); // 출력 버퍼의 크기를 10으로 설정

    printf("Hello, world!\n");

    delay(3); // 3초간 기다림

    return 0;
}

// Hello, wor(3초 뒤 ld!까지 출력)


  • 위의 코드를 실행해 보면 먼저 “Hello, wor”까지만 나오다가 3초가 지나면 남은 “ld!”까지 모두 출력된다.
  • 이렇게 출력되는 이유는 setvbuf(stdout, NULL, _IOFBF, 10);과 같이 표준 출력 stdout의 출력 버퍼를 10으로 설정했기 때문이다.
  • 따라서 printf(”Hello, world!\n”);로 출력해도 버퍼 크기 10만큼 “Hello, wor”가 출력되고 3초가 지난 뒤 버퍼가 비워져서 “ld!”까지 모두 출력된다.
  • setvbuf 함수의 첫 번째 인수에는 입출력 버퍼의 설정을 변경할 파일 포인터(파일 스트림)을 넣어준다.
  • stdin, stdout, stderr도 파일 포인터이므로 그대로 넣으면 된다.
  • 두 번째 인수에는 입출력 버퍼로 사용할 배열(메모리)을 넣는데 NULL을 지정하면 내부적으로 버퍼 공간을 생성한다.
  • 세 번째 인수는 버퍼링 모드이고, 네 번째 인수는 입출력 버퍼 크기이다.


1] _IOFBF

  • Full Buffering, 버퍼가 가득 차면 버퍼를 비운다.

2] _IOLBF

  • Line Buffering, \n을 만나거나 버퍼가 가득 차면 버퍼를 비운다.

3] _IONOBUF

  • No Buffering, 버퍼를 사용하지 않으며 입출력 버퍼로 사용할 배열(메모리)과 크기는 무시된다.


  • 위의 코드에서는 출력 버퍼가 비워지는 시점을 조절하기 위해 특정 시간(초)만큼 기다리는 함수 delay를 만들어서 사용했다.
  • 실제로는 입출력 버퍼가 비워지는 시점을 조절할 상황은 많지 않을 것이다.
  • 출력 버퍼를 강제로 비우려면 다음과 같이 fflush를 사용하면 된다.


fflush(파일포인터);
성공하면 0 반환, 실패하면 EOF(-1) 반환
#include <stdio.h>
#include <time.h>

void delay(unsigned int sec) // 특정 시간(초)만큼 기다리는 함수
{
    clock_t ticks1 = clock();
    clock_t ticks2 = ticks1;
    while ((ticks2 / CLOCKS_PER_SEC - ticks1 / CLOCKS_PER_SEC) < (clock_t)sec)
        ticks2 = clock();
}

int main()
{
    setvbuf(stdout, NULL, _IOFBF, 10); // 출력 버퍼의 크기를 10으로 설정

    printf("Hello, world!\n");
    fflush(stdout); // 표준 출력의 출력 버퍼를 강제로 비움

    delay(3); // 3초간 기다림

    return 0;
}

// Hello, world!


  • 앞에서는 3초가 지난 뒤에 “Hello, world!”가 모두 출력되었지만 fflushstdout의 출력 버퍼를 강제로 비우면 “Hello, world!”가 즉시 출력된다.

References