在执行 scanf 之前 printf 如何被刷新?

use*_*289 3 c linux printf scanf buffering

我最近阅读了很多关于标准输出缓冲的内容。我知道它printf是缓冲的,但到目前为止我认为它的缓冲区只有在新行被读入缓冲区或被fflush(stdout)调用或调用的进程printf正常退出时才会被刷新。

我写了这个程序,它调用 printf 之前没有换行scanf。当我用谷歌搜索时,我发现很多人说他们不明白为什么在 printf 之前执行 scanf。由于我现在了解标准输出缓冲的概念,这对我来说很有意义。

但是,就我而言,缓冲区在我运行 scanf 之前被刷新。这样做确实有意义,因为用户可能希望在任何 scanf 之前执行 printf,但它是如何发生的?究竟什么是刷新标准输出?是scanf吗?

int main(void) {
    char things;
    printf("Hello ");
    scanf("%c", &things);
}
Run Code Online (Sandbox Code Playgroud)

(我正在运行 Arch Linux)

编辑:由于一些评论说我的系统的标准输出是无缓冲的,我只想添加它而不scanf在我的程序上运行,我的程序完全具有我上面提到的行为,它肯定是缓冲的。

chq*_*lie 6

这是一个实施质量问题。

C 标准仅强制要求,stdin并且stdout仅在附加到常规文件时才在默认情况下完全缓冲。但它明确鼓励交互式设备的特定行为:

5.1.2.3 程序执行
[...]
对一致实现的最低要求是:
[...]
交互设备的输入和输出动态应按照 7.21.3 的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保在程序等待输入之前实际出现提示消息。

在许多 Posix 系统上,stdin并且stdout在附加到字符设备时被行缓冲,stdout然后在读取尝试stdin需要从底层系统句柄读取时自动刷新。即使没有尾随换行符,这也允许提示出现在终端上。

在 linux 上,此行为在stdio(3) linux 手册页中指定

默认情况下,引用终端设备的输出流总是行缓冲的;每当读取引用终端设备的输入流时,都会自动将挂起的输出写入此类流。如果在输出终端打印部分行后进行大量计算,则需要在关闭和计算之前对标准输出进行 fflush(3) 以便输出出现。

然而,GNU libc 有一个微妙的不同行为:仅stdout按照glibc/libio/fileops.c 中的编码(由 Ulrich Drepper 在 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
    }
Run Code Online (Sandbox Code Playgroud)