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

醉酒当歌 提交于 2020-01-25 09:33:57

除非换行符在格式字符串中,否则为什么在调用后printf不会刷新? 这是POSIX行为吗? 每次如何立即使printf刷新?


#1楼

stdout已缓冲,因此仅在换行符输出后输出。

要立即获得输出,请执行以下任一操作:

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

#2楼

要立即刷新,请调用fflush(stdout)fflush(NULL)NULL表示刷新所有内容)。


#3楼

您可以使用fprintf到没有缓冲的stderr。 或者,您可以在需要时刷新标准输出。 或者,您可以将stdout设置为unbuffered。


#4楼

默认情况下, stdout流是行缓冲的,因此仅在到达换行符后(或被告知时)才显示缓冲区中的内容。 您有几种选择可以立即打印:

打印到stderr而不是使用fprintf默认情况下 stderr是未缓冲的 ):

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

在需要使用fflush时冲洗stdout:

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

编辑 :从下面的安迪·罗斯的评论,您还可以通过使用setbuf禁用对stdout的缓冲:

setbuf(stdout, NULL);

#5楼

可能是因为效率高,并且因为如果您有多个程序写入一个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

请注意,甚至不能保证在换行符上进行刷新,因此,如果刷新很重要,则应显式刷新。


#6楼

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


#7楼

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


#8楼

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

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


#9楼

不,这不是POSIX行为,它是ISO行为(嗯,这 POSIX行为,但仅在它们符合ISO的范围内)。

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

myprog >myfile.txt

这对于提高效率很有意义,因为如果您与用户互动,他们可能希望查看每一行。 如果将输出发送到文件,则很可能另一端没有用户(尽管并非不可能,但他们可能会拖尾文件)。 现在您可以争辩说用户希望看到每个字符,但这有两个问题。

首先是效率不是很高。 第二个原因是,最初的ANSI C指令主要是为了整理现有的行为,而不是发明新的行为,而这些设计决策是在ANSI开始该过程之前就做出的。 如今,在更改标准中的现有规则时,即使是ISO现在也要非常谨慎。

至于如何处理,如果您在要立即看到的每个输出调用之后都进行了fflush (stdout) ,则可以解决此问题。

另外,您也可以使用setvbuf在操作之前stdout ,将其设置为无缓冲,你会不会有关于添加所有这些担心fflush行到你的代码:

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

只要记住这可能会影响性能相当多,如果你发送的输出到文件。 还要记住,对此的支持是实现定义的,标准并不能保证。

ISO C99第7.19.3/3节是相关的位:

当流没有缓冲时 ,字符应尽快从源或目标出现。 否则,字符可能会作为块被累积并传输到主机环境或从主机环境传输。

当流被完全缓冲时 ,打算在填充缓冲区时将字符作为块与主机环境进行传输。

当流被行缓冲时 ,当遇到换行符时 ,字符打算作为块与主机环境进行传输。

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

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


#10楼

通常有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() ,使内核缓冲区中的数据可用后立即启动写操作。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!