I thought that its buffer only gets flushed when a new line is read into the buffer or fflush(stdout)
is called or the process that called printf
exited normally.
That is not the intent of the C standard. When a stream is line buffered, output should also be flushed whenever the program requests input on any unbuffered stream or on a line buffered stream that is taking input from “the host environment” (such as a terminal window where the user types input), as stated in C 2018 7.21.3 3:
… When a stream is line buffered, characters are intended to be transmitted to or from the host environment as a block when a new-line character is encountered. Furthermore, characters are intended to be transmitted as a block to the host environment when a buffer is filled, when input is requested on an unbuffered stream, or when input is requested on a line buffered stream that requires the transmission of characters from the host environment…
This only expresses an intent, and the standard further says support for these characteristics is implementation-defined, so this is technically a quality-of-implementation issue. However, it is not a quality issue in the sense of how good your diagnostic messages are. The reservations about support being implementation-defined and about, as discussed below, whether standard output is line-buffered are largely concessions to feasibility or possibility in various old computer systems. In most modern C implementations, a C implementation should not use this license by the C standard as an excuse not to implement these features.
Here is an example of how reading input from an unrelated stream can flush standard output. When I execute this program on macOS 10.14.6 using Xcode 11.3.1, the “Hello” in standard output is flushed when the unrelated stream to /dev/null
is read, but not when output is merely written with printf
with no read:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Hello");
FILE *dummy = fopen("/dev/null", "r");
setvbuf(dummy, NULL, _IONBF, 0); // Make dummy unbuffered.
fgetc(dummy); // "Hello" appears on terminal.
printf(" world."); // " world." does not appear on terminal.
sleep(5);
printf("\n"); // " world." appears on terminal.
}
If we remove either the setvbuf
or the fgetc
, the “Hello” does not appear on the terminal immediately, demonstrating that it is a read to the unbuffered stream that causes standard output to be flushed.
I'm aware that printf
is buffered…
This depends on circumstances. It is actually the stream that is buffered or not, and C 2018 7.21.3 7 says:
… the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device.
Thus, if you redirect a program’s standard output to a file, it does not refer to an interactive device, so it must be fully buffered if the program can detect this. When a program’s output is going to an interactive terminal window, then it must not be fully buffered. (The alternatives are line buffered and unbuffered, and typical C implementations use line buffered.)'
So, if standard input and standard output are both connected to an interactive device (and the C implementation cannot detect otherwise), then printf
output should appear before scanf
is executed, either because standard output is unbuffered (so the printf
output appears immediately) or because standard output is line buffered and is flushed when scanf
is called.