C / CONSOLE INPUT AND OUTPUT
Output buffering and when to flush stdout
Predict when printf output actually leaves your program, and use fflush and setvbuf so prompts, progress and crash-time debug lines appear on time.
What you will learn
- Predict whether stdout is line buffered or fully buffered from where it is pointing
- Flush with fflush(stdout) after a prompt that has no trailing newline
- Force _IONBF, _IOLBF or _IOFBF with setvbuf before the stream's first write
- Explain why the last printf vanishes after _Exit, abort or a segfault
Understanding Output buffering and when to flush stdout
printf does not talk to your terminal. It copies bytes into a buffer that lives inside your own process, and that buffer is handed to the operating system in a single write only when something drains it: the buffer fills up, a newline arrives while the stream is line buffered, you call fflush, or the stream is closed during normal program termination. The reason for all this machinery is cost. Copying a few bytes into memory takes nanoseconds while a system call takes microseconds, so a loop of ten thousand small printf calls becomes a handful of large writes instead of ten thousand tiny ones.
Which draining rule applies is decided when the stream is first used, based on what stdout is attached to. Attached to a terminal it is line buffered, so each newline pushes a line out and interactive programs feel responsive. Redirected into a file or a pipe it is fully buffered with a buffer of a few kilobytes, so nothing appears until the buffer fills or the program exits. stderr is never fully buffered, which is why error messages survive a crash that swallows the printf output around them, and why running a program as ./prog > log 2>&1 can put a diagnostic above lines that were printed before it.
So flush at the points where waiting is wrong or where the buffer may never be drained at all: after a prompt with no trailing newline and before you block on input, before a slow phase whose progress you want to watch, before abort, _Exit, raise or an exec that replaces the process, and before handing the same terminal to a child process. Everywhere else leave it alone, because returning from main or calling exit flushes every open stream for you, and an fflush after every line throws away exactly the batching the buffer exists to provide. If you genuinely need every byte to appear the instant it is written, say that once with setvbuf(stdout, NULL, _IONBF, 0) rather than scattering flushes through the code.
placeholder
<stdio.h>
<stdlib.h>
int main(void)
{
/* Force full buffering so this behaves the same on a terminal,
a pipe or a file. Must come before the first write to stdout. */
setvbuf(stdout, NULL, _IOFBF, BUFSIZ);
printf("A: copied into the buffer\n");
printf("B: copied into the buffer too\n");
fflush(stdout); /* A and B leave the process here, in one write */
printf("C: still in the buffer\n");
_Exit(0); /* no stdio cleanup, so C dies with the process */
}
Bytes written with printf sit in a buffer inside your own process until a newline in line-buffered mode, a full buffer, an explicit fflush, or normal termination drains it.
Worked examples
Why stderr jumps ahead of stdout
With both streams going to the same console, a fully buffered stdout makes a later diagnostic appear before earlier output.
<stdio.h>
int main(void)
{
setvbuf(stdout, NULL, _IOFBF, BUFSIZ); /* what redirection gives you */
printf("stdout: line 1\n");
fprintf(stderr, "stderr: disk is full\n");
printf("stdout: line 2\n");
return 0; /* stdout's buffer is flushed during exit */
}
Example explained
Line 1setvbuf with _IOFBF reproduces the mode stdout gets automatically when it points at a file or pipe.
Line 2The fprintf reaches the console at once because stderr is not fully buffered.
Line 3Both stdout lines wait in the buffer until return from main, so they land after a message that was written later.
Line 4An fflush(stdout) before the fprintf puts the three lines back in program order.
Unbuffered output for a progress line
Turning buffering off makes partial lines with no newline appear as soon as they are printed.
<stdio.h>
int main(void)
{
setvbuf(stdout, NULL, _IONBF, 0); /* every printf goes straight out */
for (int i = 1; i <= 3; i++)
printf("step %d... ", i);
printf("done\n");
return 0;
}
Example explained
Line 1_IONBF removes the buffer, so each printf hands its bytes to the operating system on its own write.
Line 2The three step strings contain no newline and still appear one at a time; nothing is waiting for a '\n'.
Line 3The price is one system call per printf, which is acceptable for a progress line and wasteful inside a hot loop.
Important notes
setvbuf may be used only before the first read or write on that stream, and a buffer you supply yourself must outlive the stream; an automatic array in a function that returns leaves stdio writing into dead memory.
_Exit is used above because the mainstream C libraries do not flush through it, though the standard leaves that implementation-defined; fflush(NULL) is the portable way to drain every output stream before something risky.
Common mistakes
Printing a prompt such as printf("Name: ") and reading immediately with no fflush: the prompt is still in the buffer, so the program looks hung on a blank line. It appears to work on glibc terminals only because glibc flushes line-buffered streams before reading, an implementation detail that stops helping the moment stdout is redirected.
Calling fflush(stdin) to get rid of leftover typed characters: flushing an input stream is undefined behaviour in ISO C. It is a documented extension on some Windows runtimes and quietly does nothing useful elsewhere, so the code looks portable and is not.
Debugging by printf and trusting the last line printed: on a crash or abort the buffer dies unwritten, so the real point of failure is further along than the output suggests, and hours go into the wrong function. Flush after each trace line, or write traces to stderr.
Try it yourself
Change, predict, then run
Write a program that calls setvbuf(stdout, NULL, _IOFBF, BUFSIZ), prints three numbered lines and ends with _Exit(0), then move a single fflush(stdout) between the printf calls and confirm that exactly the lines written before the flush appear.
Open the C workspaceCheck your understanding
A program prints "Working..." with printf (no newline), spends five seconds in a loop, then prints "done\n". Run in a terminal, nothing shows up for five seconds. Which explanation and fix are right?
- Call fflush(stdout) after the first printf: a line-buffered stream is drained only by a newline, a full buffer or an explicit flush.
- Use puts instead of printf, because puts bypasses the stdio buffer and writes straight to the terminal.
- Nothing can be done from C; the terminal driver decides when characters become visible.
- Call fflush(stdin) before the loop so that stdio stops buffering stdout.
Show answer
The text is sitting in stdout's buffer and "Working..." contains no newline, so only an explicit fflush (or the exit at the end of the program) can push it out. The puts answer is tempting because swapping in puts often does make the text appear, but not for the reason given: puts writes into the very same buffer and only seems to help because it appends a newline, and with stdout redirected to a file that newline no longer flushes anything. fflush(stdin) is undefined behaviour on an input stream and has no effect on stdout at all.