87. 입출력 버퍼
1. 키워드
- 입출력 버퍼
- 버퍼링(Buffering)
2. 입출력 버퍼 활용하기
- C의 입출력 함수들은 내부적으로 입출력 버퍼를 사용하여 데이터를 처리한다.
- 입출력 버퍼는 겉으로 드러나지 않으며 다음과 같이 함수를 사용할 때 선언하는 변수
buffer
는 입출력 버퍼가 아니다. - 프로그래머 입장에서 데이터를 임시로 저장한다고 해서 버퍼라고 이름을 지은 것이다.
- 표준 입력(키보드)은 입력되는 문자를 입력 버퍼에 저장했다가 엔터 키(
\n
)가 입력되면 지정된 변수(배열, 할당한 메모리)로 옮긴다. - 마찬가지로 표준 출력(화면)은 출력 버퍼에 문자가 저장되었다가 특정 조건에 의해 버퍼가 비워지면 화면에 출력된다.
- 입출력 버퍼가 비워지는 시점은 OS나 설정에 따라 달라진다.
- 즉, 프로그램과 OS는 데이터를 효율적으로 처리하기 위해 입출력 버퍼를 사용한다.
- 만약 입출력 버퍼 없이 데이터를 1바이트씩 처리하면 입출력이 될 때마다 매번 데이터를 처리해야 되므로 CPU 사용 횟수와 메모리 접근 횟수도 많아진다.
- 하지만 입출력 버퍼를 사용하면 일정 크기만큼 데이터를 모아두었다가 처리하므로 그만큼 CPU를 덜 사용하고 메모리 접근 횟수도 줄어든다.
- 이처럼 입출력 버퍼에 데이터를 저장하는 행동을 버퍼링이라고 부른다.
- 이제 입출력 버퍼가 동작하는 모양을 살펴보자.
- 다음은 출력 버퍼의 크기를 설정한 뒤
printf
로 문자열을 출력한다.
#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
를 사용하면 된다.
#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!”
가 모두 출력되었지만fflush
로stdout
의 출력 버퍼를 강제로 비우면“Hello, world!”
가 즉시 출력된다.