Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is printf getting flushed before scanf is executed?

I have recently read a lot about standard output buffering. I'm aware that printf is buffered but so far 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 printfexited normally.

I wrote this program which calls printf without a new line just before scanf. When I googled that, I found a lot of people saying they didn't understand why scanf was executed before printf. Since I now understand the concept of stdout buffering, that makes sense to me.

However, in my case, the buffer is flushed just before I run scanf. It does make sense to do so because the user probably wants the printf to be executed before any scanf, but how is it happening? What exactly is flushing stdout? Is it scanf?

int main(void) {
    char things;
    printf("Hello ");
    scanf("%c", &things);
}

(I am running Arch Linux)

Edit: Due to some comments saying my system's stdout is unbuffered, I just want to add that without running scanfon my program, my program has exactly the behaviour I mentioned above, it is definitely buffered.

like image 847
user13024289 Avatar asked Dec 14 '22 07:12

user13024289


1 Answers

This is a quality of implementation issue.

The C Standard only mandates that stdin and stdout be fully buffered by default only when attached to a regular file. But it explicitly encourages specific behavior for interactive devices:

5.1.2.3 Program execution
[...]
The least requirements on a conforming implementation are:
[...]
The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.

On many Posix systems, stdin and stdout are line buffered when attached to a character device and stdout is then flushed automatically when a read attempt from stdin requires reading from the underlying system handle. This allows the prompt to appear on the terminal even without a trailing newline.

On linux, this behavior is specified in the stdio(3) linux manual page:

Output streams that refer to terminal devices are always line buffered by default; pending output to such streams is written automatically whenever an input stream that refers to a terminal device is read. In cases where a large amount of computation is done after printing part of a line on an output terminal, it is necessary to fflush(3) the standard output before going off and computing so that the output will appear.

Yet the GNU libc has a subtly different behavior: only stdout is flushed this way as coded in glibc/libio/fileops.c (as modified by Ulrich Drepper on 2001-08-04 23:59:30):

  /* Flush all line buffered files before reading. */
  /* FIXME This can/should be moved to genops ?? */
  if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
    {
#if 0
      INTUSE(_IO_flush_all_linebuffered) ();
#else
      /* We used to flush all line-buffered stream.  This really isn't
         required by any standard.  My recollection is that
         traditional Unix systems did this for stdout.  stderr better
         not be line buffered.  So we do just that here
         explicitly.  --drepper */
      _IO_acquire_lock (_IO_stdout);

      if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))
          == (_IO_LINKED | _IO_LINE_BUF))
        _IO_OVERFLOW (_IO_stdout, EOF);

      _IO_release_lock (_IO_stdout);
#endif
    }
like image 196
chqrlie Avatar answered Dec 18 '22 00:12

chqrlie