C Stream Buffering: fflush, setbuf, and setvbuf
Standard I/O in the C programming language uses stream buffering to
optimize input and output operations by reducing the number of direct,
expensive system calls. This article explains how stream buffering
operates under the hood, details the differences between unbuffered,
line-buffered, and fully buffered streams, and demonstrates how to
control and flush these buffers using the standard library functions
setvbuf, setbuf, and fflush.
How Standard Stream Buffering Works
When a program reads from or writes to a FILE pointer in
C, the standard I/O library (stdio.h) does not immediately
trigger an operating system read or write system call for every
character. Instead, data is accumulated in an intermediate memory area
called a buffer.
C defines three distinct buffering modes:
- Fully Buffered (
_IOFBF): Characters are stored in the buffer and only transmitted to the destination when the buffer fills up completely. This is the default behavior for file-based streams. - Line Buffered (
_IOLBF): Characters are collected and transmitted whenever a newline character (\n) is written or when the buffer becomes full. This is the standard behavior for interactive devices, such asstdoutconnected to a terminal. - Unbuffered (
_IONBF): Characters are transmitted directly to their destination as soon as they are written, without temporary storage. By default, standard error (stderr) is unbuffered so diagnostic messages appear immediately.
Customizing Buffers with
setvbuf
The setvbuf function is the preferred, robust interface
for controlling stream buffering behavior. It allows you to specify the
stream, the buffering mode, an optional user-allocated buffer, and the
buffer's size.
Function Signature
int setvbuf(FILE *stream, char *buffer, int mode, size_t size);Parameters and Usage
stream: The pointer to the openFILEstream to configure.buffer: A pointer to a user-allocated memory block to serve as the buffer. If set toNULL, the standard library automatically allocates an internal buffer of the requestedsize.mode: The buffering mode, chosen from_IOFBF,_IOLBF, or_IONBF.size: The size in bytes of the buffer (ignored if mode is_IONBF).
setvbuf must be called immediately after opening the
stream and before performing any I/O operations. It returns
0 on success, or a non-zero value on failure (such as an
invalid mode or invalid size).
Example
#include <stdio.h>
int main(void) {
// Disable buffering on stdout completely
setvbuf(stdout, NULL, _IONBF, 0);
// Set a custom 4KB buffer with full buffering for a file
FILE *file = fopen("output.log", "w");
if (file) {
char custom_buffer[4096];
setvbuf(file, custom_buffer, _IOFBF, sizeof(custom_buffer));
fputs("Buffered file output\n", file);
fclose(file);
}
return 0;
}Note: If providing a user-allocated array (like
custom_buffer), ensure its lifetime exceeds the lifetime of
the stream, or allocate it dynamically to avoid undefined behavior
caused by out-of-scope stack memory.
Legacy Buffering with
setbuf
The setbuf function is an older, simplified wrapper
around setvbuf.
Function Signature
void setbuf(FILE *stream, char *buffer);Mechanics
Passing a buffer pointer to setbuf is equivalent to:
(void)setvbuf(stream, buffer, _IOFBF, BUFSIZ);Passing NULL disables buffering entirely, equivalent
to:
(void)setvbuf(stream, NULL, _IONBF, 0);setbuf does not provide a mechanism to specify custom
buffer sizes (it assumes the implementation-defined constant
BUFSIZ) and does not return status codes to indicate
failure. For modern C development, setvbuf is preferred
over setbuf.
Forcing Buffer Writes with
fflush
While buffers normally empty automatically when full, on a newline
(in line-buffered mode), or when the stream is closed via
fclose, you often need to force pending writes to disk or
the terminal manually. This is managed by fflush.
Function Signature
int fflush(FILE *stream);Behavior
- Output Streams: Calling
fflushon an output or update stream writes any unwritten data from the stream's user-space buffer to the underlying operating system file descriptor. - All Open Streams: Passing
NULLas thestreamargument (fflush(NULL)) flushes all open output streams across the entire program. - Return Value: Returns
0on success, orEOFif a write error occurs.
Input Stream Undefined Behavior
According to the ISO C standard, calling fflush on an
input stream (such as stdin) results in
undefined behavior. While some platform-specific
extensions (such as Microsoft's MSVC runtime) define behavior to discard
unread input, it is non-portable and should not be used in standard
C.
Example
#include <stdio.h>
#include <unistd.h>
int main(void) {
// stdout is typically line-buffered; without a newline, text remains in the buffer
printf("Processing data, please wait...");
// Explicitly write the buffer contents to the terminal immediately
fflush(stdout);
// Simulate work
sleep(2);
printf(" Done!\n");
return 0;
}