문자열을 출력하는 과정 : 문자열->출력버퍼->출력버퍼 비움과 동시에 화면에 출력
위와 같이 이해했습니다.
그러면 이 함수는 표준출력 버퍼를 임시로 만든 버퍼를 경유하여 화면에 출력하기 위한 함수라고 이해하면 되나요?
setvbuf(stdout, NULL, _IOFBF, 10);
10바이트만큼의 버퍼를 생성하고 화면에 출력됨과 동시에 버퍼가 비워지고 나머지 문자열을 가져온 후 다시 화면에 출력되는 방식인 것 같은데, 여기서 질문입니다.
#define _CRT_SECURE_NO_WARNINGS
#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);
printf("Hello, world!\n");
delay(3);
return 0;
}
위 코드에서 printf함수는 "Hello, world!\n"라는 문자열을 stdout 파일 포인터로 옮기고 이후 내부적으로 생성된 10바이트 크기의 버퍼로 이동한 후 "Hello, wor"가 화면에 출력됨과 동시에 버퍼가 비워진 후 나머지 문자열 "ld!\n"가 출력되는데 delay함수가 printf 함수 뒤에 있음에도 불구하고 두 출력 사이에 딜레이가 생기는 이유는 무엇인가요?
setvbuf(stdout, NULL, _IOFBF, 3);
와 같이 버퍼 크기를 3바이트로 설정하고 다시 컴파일해 보니 이번에는
"Hello, world"까지 한번에 출력되고 3초 기다린 후 나머지 "!\n"이 출력되었습니다. 딜레이가 왜 저렇게 걸리는지 궁금해서 디버그 모드에서 단계별로 실행해 본 결과 main함수가 종료될 때 "!\n"가 출력되었습니다.
버퍼가 가득 차면 즉시 출력되고 남은 문자열이 버퍼의 크기보다 작으면 프로그램이 종료될 때 버퍼가 비워짐과 동시에 나머지가 출력되는 건가요?
delay 함수에서 3초 뒤에 출력하라고 했으니 3초 뒤에 출력되는 것은 당연합니다.
size 크기가 너무 작으면 내부적으로 할당된 최소 크기의 버퍼가 이용되는 것이고, 이는 컴파일러 구현체에 따라 다를 것입니다.
https://docs.microsoft.com/ko-kr/cpp/c-runtime-library/reference/setvbuf?view=msvc-160
size
버퍼 크기(바이트)입니다. 허용 범위: 2 <= size <= INT_MAX (2147483647). 내부적으로 크기 에 대해 제공 되는 값은 가장 가까운 2의 배수로 반올림 됩니다.
Visual C++에서는 허용 범위를 저렇게 제시하고 있지만, size를 2바이트로 적용해도 12바이트가 최소로 적용된 것으로 보입니다.
int main()
{
char* buf = malloc(1024);
memset(buf, 0, 1024);
setvbuf(stdout, buf, _IOFBF, 6); // 출력 버퍼의 크기를 10으로 설정
printf("Hello, world!\n");
delay(3); // 3초간 기다림
return 0;
}
이와 같이 변경해서 테스트해보면 setvbuf에서 8바이트까지 설정했을 때는 8바이트까지 출력되는 것을 확인할 수 있지만, 6으로 지정하면 12바이트까지 출력되는 것을 확인할 수 있습니다.
size의 범위는 2 이상이라고 되어 있지만, 내부적으로는 동작이 저렇게 되는 것으로 보입니다.
_IOFBF이면 IO 입출력을 FBF, Fully Buffered로 처리하게 됩니다.
이 경우 바로 입출력을 처리하지 않고, 버퍼에 잠시 저장된 이후에 처리됩니다. 약간의 시간 지연이 발생합니다.
따라서 delay 함수가 없으면 문자열의 일부만 출력된 상태에서 나머지 버퍼는 비워지지 않고 프로그램이 종료되기 때문에 전체 문자열이 출력되지 않습니다.
버퍼 모드를 _IOLBF, _IONOBUF로 바꿔서 테스트해보면 됩니다. 다만, 일반적으로 사용하는 함수는 아니니 깊이 들어갈 필요는 없습니다. scanf를 연속해서 사용하면 문제가 발생하는 원인을 설명하기 위해 입출력 버퍼를 설명한 것입니다. scanf에서 %s를 연속으로 처리할 경우 엉망이 되는 것인데, 대다수 책에서는 이유를 설명하지 않고, %s 입력을 한 번만 처리하는 것으로 문제를 가리는 경우가 많습니다. 그렇다고 문제를 가리지 않고 설명하려면 setvbuf까지 설명해야 합니다.
요약하자면
최소 버퍼 크기는 컴파일러 구현체에 따라 다르다. 너무 작은 크기는 지정하지 말자.
버퍼링 모드에서는 일정 시간 동안 버퍼링을 하기 때문에 시간 지연이 있어야 프로그램 종료 전에 버퍼링이 끝나는 것을 보장할 수 있다. 그래서 시간 지연 코드를 넣은 것일 뿐이다.