为什么printf
在调用后不会刷新,除非换行符在格式字符串中?这是POSIX的行为吗?我怎么可能每次都printf
立即冲洗?
答案 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 时,字符应尽快从源或目的地出现。否则,字符可以作为块累积并传输到主机环境或从主机环境传输。
当流完全缓冲时,在填充缓冲区时,字符应作为块传输到主机环境或从主机环境传输。
当流行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。
此外,当填充缓冲区,在无缓冲流上请求输入时,或者在需要传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境。主机环境。
对这些特征的支持是实现定义的,可能会受到
setbuf
和setvbuf
函数的影响。
答案 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")
:
答案 5 :(得分:11)
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()
之类的其他功能,使内核缓冲区中的数据可用后立即使内核启动写操作。