printf
在调用后没有换行符的情况下不自动刷新输出缓冲区?这是否符合POSIX标准?如何让printf
每次都立即刷新缓冲区?默认情况下,stdout
流是按行缓冲的,所以只有在遇到换行符(或被告知时)后才会显示缓冲区中的内容。你有几个选项可以立即打印:
使用fprintf
将输出打印到stderr
而不是stdout
(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
禁用标准输出的缓冲:
setbuf(stdout, NULL);
或者使用更灵活的setvbuf
函数:
setvbuf(stdout, NULL, _IONBF, 0);
setbuf(stdout, NULL);
。 - Andy Rosssetlinebuf()
会在每行末尾进行刷新。 - Doddy不,这不是POSIX行为,而是ISO C行为(实际上,只有在符合ISO C时才是POSIX行为)。
如果可以检测到标准输出是交互设备,则其为行缓冲,否则为完全缓冲。因此,在某些情况下,即使printf
获得了要发送的换行符,它也不会刷新,例如:
myprog >myfile.txt
这种做法在效率上是有道理的,因为如果你与用户交互,他们可能希望看到每一行。如果将输出发送到文件,则很可能在另一端没有用户(虽然不是不可能,他们可能正在关注该文件)。现在你可以争辩说用户想要看到每个字符,但存在两个问题。
第一,这不是很高效。 第二,原始的 ANSI C 89 要求主要是为了编码现有行为,而不是发明新行为,并且那些设计决策是在 ANSI 开始进程之前做出的。即使现在的 ISO C 在更改标准中的现有规则时也非常小心谨慎。
至于如何处理这个问题,如果你在每个要立即查看的输出调用之后使用fflush(stdout)
,那么问题就解决了。
或者,您可以在操作stdout
之前使用setvbuf
,将其设置为无缓存,这样您就不必担心将所有这些fflush
行添加到代码中:
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
请记住,如果您将输出发送到文件,则可能会严重影响性能。还要记住,对此的支持是实现定义的,而不是标准保证。
ISO C99第7.19.3/3
节是相关部分:
myprog >/tmp/tmpfile
重定向输出,则它是完全缓冲而不是行缓冲。我记得,确定您的标准输出是否为交互式是由实现来决定的。 - paxdiablo这可能是为了效率考虑,同时如果有多个程序向单个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
请注意,甚至不能保证在换行时刷新缓冲区,因此如果刷新对您很重要,则应明确进行刷新。fflush(stdout)
或fflush(NULL)
(NULL
表示刷新所有内容)。fflush(NULL);
通常是一个非常糟糕的想法。如果您打开了许多文件,特别是在多线程环境中,将与所有内容竞争锁定,这将影响性能。 - R.. GitHub STOP HELPING ICEstdout是有缓冲的,只有在打印换行符后才会输出。
要立即输出,可以:
fflush(stdout)
。 - RastaJedistderr
(本回答稍后提到)、fflush(stdout)
、fflush(NULL)
。 - chux - Reinstate Monica注意:Microsoft运行时库不支持行缓冲,因此printf("将立即打印到终端")
:
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf
printf
和fprintf
的缓冲也会更加粗略。除非微软已经解决了这个问题,否则这将使得一个程序无法捕获另一个程序的标准错误和标准输出,并确定发送到每个流的顺序。 - supercat默认情况下,标准输出是行缓冲的,标准错误输出是无缓冲的,文件则是完全缓冲的。
setbuf(stdout, NULL);
来禁用缓冲。一般有两种缓冲级别 -
1. 内核缓存(使读/写更快)
2. I/O库中的缓冲(减少系统调用次数)
让我们以fprintf和write()
为例。
当您调用fprintf()
时,它不会直接写入文件。它首先进入程序内存中的stdio缓冲区。从那里通过使用写入系统调用将其写入内核缓存。因此,跳过I/O缓冲区的一种方法是直接使用write()。其他方法是使用setbuff(stream,NULL)
。这将缓冲模式设置为无缓冲,并且数据直接写入内核缓冲区。
为了强制数据转移到内核缓冲区,我们可以使用“\n”,在默认的“行缓冲”缓冲模式下,将刷新I/O缓冲区。
或者我们可以使用fflush(FILE * stream)
。
现在我们在内核缓冲区中。内核(/操作系统)想要最小化磁盘访问时间,因此只读取/写入磁盘块。因此,当发出read()
(一种系统调用,可以直接调用或通过fscanf()
调用)时,内核从磁盘中读取磁盘块并将其存储在缓冲区中。之后将数据从这里复制到用户空间。
类似地,fprintf()
从I/O缓冲区接收的数据被内核写入磁盘。这使得read() write()更快。
现在为了强制内核启动write()
,之后数据传输由硬件控制器控制,还有一些方法。我们可以在写入调用期间使用O_SYNC
或类似标志。或者我们可以使用其他函数如fsync(),fdatasync(),sync()
,使内核在数据可用于内核缓冲区时立即启动写入。
setvbuf(stdout, (char*)NULL, _IONBF, 0)
可以解决这个问题,但这显然不应该是必须的。我正在使用MSVC++ 2008 Express。 - Steve Pitchersprintf(..)
本身不会执行任何刷新操作,它是stdout
缓冲区在看到换行符时可能会刷新的结果(如果是行缓冲)。对于putchar('\n')
,它将以同样的方式作出反应,因此在这方面printf(..)
并不特别。这与cout << endl;
不同,其文档明确提到了刷新。printf的文档并没有提到刷新。 - Evgeni Sergeev