我最近读了很多关于标准输出缓冲的文章。我知道printf
是缓冲的,但到目前为止,我认为只有当一个新行被读入缓冲区,或者fflush(stdout)
被调用,或者调用printf
的进程正常退出时,它的缓冲区才会被刷新。
我编写了这个程序,它调用printf,在scanf
之前没有一个新行。当我在googled上搜索时,我发现很多人说他们不明白为什么scanf在printf之前就被执行了。既然我现在理解了stdout缓冲的概念,这对我来说是有意义的。
但是,在我的例子中,缓冲区是在我运行扫描之前刷新的。这样做是有意义的,因为用户可能希望在扫描之前执行printf,但是它是如何发生的呢?冲水的标准到底是什么?是扫描吗?
int main(void) {
char things;
printf("Hello ");
scanf("%c", &things);
}
(我正在运行Arch )
编辑:由于一些评论说我的系统的stdout是非缓冲的,我只想补充一下,如果我的程序没有运行scanf
,我的程序就有我前面提到的行为,它肯定是缓冲的。
发布于 2021-03-26 12:19:24
我认为只有当一个新行被读入缓冲区,或者
fflush(stdout)
被调用,或者调用printf
的进程正常退出时,它的缓冲区才会被刷新。
这不是C标准的意图。当流被行缓冲时,当程序请求输入任何未缓冲的流或从“主机环境”接收输入的行缓冲流(例如用户输入的终端窗口)时,也应该刷新输出,如C 2018 7.21.3 3所述:
…当流被缓冲时,当遇到新行字符时,字符将作为块传送到或从主机环境中传输。此外,在填充缓冲区时,字符将作为块传输到主机环境,在未缓冲的流上请求输入时,或在需要从主机环境传输字符的行缓冲流上请求输入时,将以的形式传输字符。
这只表达了一个意图,标准进一步说,对这些特性的支持是由实现定义的,因此这在技术上是一个实现质量问题。然而,从诊断信息的好坏来看,这并不是一个质量问题。对于支持正在实施的保留,以及如下文所讨论的,标准输出是否为行缓冲,在很大程度上是对各种旧计算机系统的可行性或可能性的让步。在大多数现代的C实现中,C实现不应该使用C标准的许可作为不实现这些特性的借口。
下面是一个示例,说明如何从不相关的流读取输入,以刷新标准输出。当我使用Xcode 11.3.1在macOS 10.14.6上执行此程序时,当读取与/dev/null
无关的流时,标准输出中的“Hello”会被刷新,而不是当输出只是用没有读取的printf
写入时:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Hello");
FILE *dummy = fopen("/dev/null", "r");
setvbuf(dummy, NULL, _IONBF, 0); // Make dummy unbuffered.
fgetc(dummy); // "Hello" appears on terminal.
printf(" world."); // " world." does not appear on terminal.
sleep(5);
printf("\n"); // " world." appears on terminal.
}
如果我们移除setvbuf
或fgetc
,“Hello”不会立即出现在终端上,这说明它是对未缓冲的流的读取,从而导致标准输出被刷新。
我知道
printf
是缓冲的…
这要视情况而定。不管是否缓冲,实际上是流,C 2018 7.21.3 7说:
…标准输入流和标准输出流被完全缓冲当且仅当可以确定流不引用交互设备时。
因此,如果将程序的标准输出重定向到文件,则它不会引用交互式设备,因此,如果程序能够检测到这一点,则必须对其进行完全缓冲。当程序输出到交互式终端窗口时,就不能完全缓冲。(备选方案有行缓冲和未缓冲,典型的C实现使用行缓冲。)
因此,如果标准输入和标准输出都连接到交互设备(而C实现不能检测到其他情况),那么在执行printf
之前应该出现scanf
输出,因为标准输出没有缓冲(因此printf
输出立即出现),或者因为标准输出是行缓冲的,并且在调用scanf
时被刷新。
https://stackoverflow.com/questions/66815208
复制相似问题