除非换行符在格式字符串中,为什么printf在调用后不会刷新?

时间:2009-11-11 16:22:39

标签: c printf flush

为什么printf在调用后不会刷新,除非换行符在格式字符串中?这是POSIX的行为吗?我怎么可能每次都printf立即冲洗?

10 个答案:

答案 0 :(得分:631)

stdout流被缓冲,因此只会在到达换行符后(或者告诉它时)显示缓冲区中的内容。您可以选择立即打印:

使用fprintf打印到stderr:

fprintf(stderr, "I will be printed immediately");

在需要时使用fflush刷新标准输出:

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer

编辑:根据下面的Andy Ross的评论,您还可以使用setbuf禁用stdout上的缓冲:

setbuf(stdout, NULL);

答案 1 :(得分:117)

不,它不是POSIX行为,它是ISO行为(好吧,它 POSIX行为,但只在它们符合ISO的情况下)。

标准输出是行缓冲的,如果可以检测到它是指交互式设备,否则它是完全缓冲的。因此,有些情况下printf不会刷新,即使它有新行发送,例如:

myprog >myfile.txt

这对效率很有意义,因为如果您与用户交互,他们可能希望看到每一行。如果您将输出发送到文件,则很可能是另一端没有用户(尽管不是不可能,但他们可能正在拖尾文件)。现在你可以认为用户想看到每个角色,但是有两个问题。

首先,它效率不高。第二个原因是ANSI C的原始授权主要是编纂现有的行为,而不是发明新的行为,而这些设计决策是在ANSI启动流程之前做出的。在改变标准中的现有规则时,即使ISO现在也非常谨慎。

至于如何处理这个问题,如果你想在每次输出调用之后fflush (stdout),那么这将解决问题。

或者,您可以在操作setvbuf之前使用stdout,将其设置为无缓冲,您不必担心将所有fflush行添加到代码中:< / p>

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

请记住,如果 将输出发送到文件,可能会影响性能。另请注意,对此的支持是实现定义的,不受标准保证。

ISO C99部分7.19.3/3是相关位:

  

当流 unbuffered 时,字符应尽快从源或目的地出现。否则,字符可以作为块累积并传输到主机环境或从主机环境传输。

     

当流完全缓冲时,在填充缓冲区时,字符应作为块传输到主机环境或从主机环境传输。

     

当流行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。

     

此外,当填充缓冲区,在无缓冲流上请求输入时,或者在需要传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境。主机环境。

     

对这些特征的支持是实现定义的,可能会受到setbufsetvbuf函数的影响。

答案 2 :(得分:27)

这可能是因为效率,因为如果你有多个程序写入单个TTY,这样你就不会在隔行扫描线上获得字符。因此,如果程序A和B正在输出,您通常会得到:

program A output
program B output
program B output
program A output
program B output

这很臭,但它比

更好
proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output

请注意,它甚至不能保证在换行符上刷新,因此如果刷新对您很重要,则应明确刷新。

答案 3 :(得分:24)

立即刷新来电fflush(stdout)fflush(NULL)NULL表示清除所有内容。)

答案 4 :(得分:15)

注意:Microsoft运行时库不支持行缓冲,因此printf("will print immediatelly to terminal")

http://msdn.microsoft.com/en-us/library/86cebhfs.aspx

答案 5 :(得分:11)

stdout是缓冲的,因此只会在打印换行符后输出。

要获得即时输出,请:

  1. 打印到stderr。
  2. 使stdout无缓冲。

答案 6 :(得分:11)

默认情况下,stdout是行缓冲的,stderr是无缓冲的,文件是完全缓冲的。

答案 7 :(得分:10)

您可以将fprintf改为stderr,而不是缓冲的。或者你可以在你想要的时候刷新标准输出。或者你可以将stdout设置为unbuffered。

答案 8 :(得分:8)

使用setbuf(stdout, NULL);禁用缓冲。

答案 9 :(得分:2)

通常有2级缓冲-

1。内核缓冲区高速缓存(使读/写速度更快)

2。在I / O库中缓冲(减少系统调用的次数)

让我们以fprintf and write()为例。

调用fprintf()时,它不会直接写入文件。它首先进入程序内存中的stdio缓冲区。使用写入系统调用从那里将其写入内核缓冲区高速缓存。因此,跳过I / O缓冲区的一种方法是直接使用write()。其他方式是使用setbuff(stream,NULL)。这会将缓冲模式设置为不缓冲,并且数据直接写入内核缓冲区。 为了强制将数据转移到内核缓冲区,我们可以使用“ \ n”,在默认缓冲模式为“行缓冲”的情况下,它将刷新I / O缓冲区。 或者我们可以使用fflush(FILE *stream)

现在我们在内核缓冲区中。内核(/ OS)希望最大程度地减少磁盘访问时间,因此它仅读/写磁盘块。因此,当发出read()(这是系统调用,可以直接或通过fscanf()进行调用)时,内核从磁盘读取磁盘块并将其存储在缓冲区中。之后,数据将从此处复制到用户空间。

类似地,内核将从I / O缓冲区接收的fprintf()数据写入磁盘。这样可以使read()write()更快。

现在强制内核启动write(),然后由硬件控制器控制数据传输,现在还有一些方法。在写调用期间,我们可以使用O_SYNC或类似的标志。或者我们可以使用fsync(),fdatasync(),sync()之类的其他功能,使内核缓冲区中的数据可用后立即使内核启动写操作。