为什么printf在调用后不刷新,除非格式字符串中有换行符?这是POSIX行为吗?如何让printf每次都立即刷新?
当前回答
注意:Microsoft运行时库不支持行缓冲,因此printf(“将立即打印到终端”):
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf
其他回答
不,它不是POSIX行为,它是ISO行为(好吧,它是POSIX行为,但只是在它们符合ISO的情况下)。
标准输出是行缓冲的,如果它可以检测到指向交互设备,否则它是完全缓冲的。所以有些情况下,printf不会刷新,即使它得到一个换行符发送出去,例如:
myprog >myfile.txt
这对于效率是有意义的,因为如果你与用户交互,他们可能想看到每一行。如果您将输出发送到一个文件,很可能另一端没有用户(尽管也不是不可能,他们可能在跟踪文件)。现在你可能会说用户想要看到每个字符,但这有两个问题。
首先是效率不高。其次,最初的ANSI C要求主要是编纂现有的行为,而不是发明新的行为,而且这些设计决策早在ANSI开始这个过程之前就已经做出了。现在,即使是ISO在改变标准中的现有规则时也非常谨慎。
至于如何处理这个问题,如果在您希望立即看到的每个输出调用之后进行fflush (stdout),就可以解决问题。
或者,你可以在操作stdout之前使用setvbuf,将其设置为unbuffered,这样你就不必担心在代码中添加所有的fflush行:
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
请记住,如果将输出发送到文件,这可能会对性能产生相当大的影响。还要记住,对此的支持是由实现定义的,而不是由标准保证的。
ISO C99第7.19.3/3节是相关的位:
When a stream is unbuffered, characters are intended to appear from the source or at the destination as soon as possible. Otherwise characters may be accumulated and transmitted to or from the host environment as a block. When a stream is fully buffered, characters are intended to be transmitted to or from the host environment as a block when a buffer is filled. When a stream is line buffered, characters are intended to be transmitted to or from the host environment as a block when a new-line character is encountered. Furthermore, characters are intended to be transmitted as a block to the host environment when a buffer is filled, when input is requested on an unbuffered stream, or when input is requested on a line buffered stream that requires the transmission of characters from the host environment. Support for these characteristics is implementation-defined, and may be affected via the setbuf and setvbuf functions.
使用setbuf(stdout, NULL);禁用缓冲。
stdout流在默认情况下是行缓冲的,因此只会在它到达换行符后(或当它被告知时)显示缓冲区中的内容。你有几个选项可以立即打印:
使用fprintf打印到stderrinstead (stderr默认是无缓冲的): fprintf(stderr,“我将立即被打印”); 在需要时使用fflush刷新标准输出: printf("Buffered, will flush "); 。fflush (stdout);//现在打印stdout缓冲区中的所有内容 使用setbuf禁用stdout的缓冲: setbuf (stdout, NULL); 或者使用更灵活的setvbuf: setvbuf(stdout, NULL, _IONBF, 0);
一般有2级缓冲-
1. 内核缓冲区缓存(使读/写更快)
2. I/O库中的缓冲(减少no。系统调用)
让我们以fprintf和write()为例。
When you call fprintf(), it doesn't wirte directly to the file. It first goes to stdio buffer in the program's memory. From there it is written to the kernel buffer cache by using write system call. So one way to skip I/O buffer is directly using write(). Other ways are by using setbuff(stream,NULL). This sets the buffering mode to no buffering and data is directly written to kernel buffer. To forcefully make the data to be shifted to kernel buffer, we can use "\n", which in case of default buffering mode of 'line buffering', will flush I/O buffer. Or we can use fflush(FILE *stream).
现在我们在内核缓冲区中。内核(/OS)希望最小化磁盘访问时间,因此它只读取/写入磁盘块。因此,当发出read()时(这是一个系统调用,可以直接或通过fscanf()调用),内核从磁盘读取磁盘块并将其存储在缓冲区中。之后,数据从这里复制到用户空间。
类似地,从I/O缓冲区接收到的fprintf()数据由内核写入磁盘。这使得read() write()更快。
现在要强制内核启动write(),之后的数据传输由硬件控制器控制,也有一些方法。我们可以在写调用期间使用O_SYNC或类似的标志。或者我们可以使用其他函数,如fsync(),fdatasync(),sync(),让内核在内核缓冲区中有数据时立即启动写操作。
你可以fprintf到stderr,这是无缓冲的。或者您可以在需要时刷新stdout。或者你可以将stdout设置为unbuffered。