【问题标题】:Is it just best to flush stdout/stderr every single time?是否最好每次都刷新标准输出/标准错误?
【发布时间】:2021-03-30 18:19:27
【问题描述】:

来自此堆栈溢出帖子:Is stdout line buffered, unbuffered or indeterminate by default?

从那篇文章中,它指出 “C99 标准没有指定三个标准流是无缓冲的还是行缓冲的:这取决于实现。”

#include <stdio.h>

int main(void) {
    printf("Enter name: ");
    fflush(stdout);
    printf("Another name: ");
    fflush(stdout);
    return 0;
}

那么,这是否意味着我们每次打印,并且我们希望确保它对用户可见,每次都刷新它是最安全的选择?由于缓冲取决于实现?

#include <stdio.h>

int main(void) {
    puts("Enter name:");
    fflush(stdout);
    puts("Another name: ");
    fflush(stdout);
    return 0;
}

即使在换行符上,每次刷新还是最好的吗?

我想 100% 确保用户看到输出,因此最好每次都刷新吗?即使在stderr?由于缓冲取决于实现?

【问题讨论】:

  • 您不能 100% 确定(并且该标准并不试图保证)(1)有用户; (2)有输出装置; (3) 输出设备(如果存在)对用户可见(如果存在)。但是如果有一个用户和他们可见的交互式设备,并且如果该交互式设备附加到三个标准流,那么标准的意图是输出写入stderr 立即可见,写入stdout 的输出在任何后续输入请求之前可见。不需要冲洗。
  • @rici 我也希望“标准的意图是......写入标准输出的输出在任何后续输入请求之前可见。”,但我没有找到 C 规范支持该声明。你知道吗?
  • @chux:第 5.1.2.3/6 节中明确的意图声明:“对一致实现的最低要求是:....交互式设备的输入和输出动态应按照 7.21 中的规定进行.3. 这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保在程序等待输入之前实际出现提示消息。”(Emph 补充)
  • @rici 谢谢。

标签: c c99 flush stdio


【解决方案1】:

我想 100% 确保用户看到输出,因此最好每次都刷新吗?

对于“100% 确保”最佳实践不是基于输出后刷新,而是基于输入前刷新stdout。当输出不以'\n' 结尾时尤其如此。

最好是fflush(stdout);

  1. 就在来自stdin 和...的输入之前

  2. 自从stdin 的先前输入以来,已经有一些输出。

更频繁地调用fflush(stdout); 不会影响功能,但可能会不必要地减慢代码速度。


fflush(stdout); 有第二种情况,即使没有输入:调试。有时,stdout 中缓冲的信息会发生致命错误。因此,fflush(stdout); 需要在可疑的错误代码之前提供,以便更好地进行分析。


即使在换行符上,每次刷新还是最好的吗?

对于“100% 确保”,在这种情况下,刷新stdout 的规则是soft enough 甚至强制fflush(stdout); 在阅读之前。

然而,stdout 在启动时应该是无缓冲的或行缓冲的(见下文),通常在这种情况下不需要刷新。


即使在标准错误上?

C 规范具有“最初打开时,标准错误流未完全 缓冲的;当且仅当可以确定流不引用交互式设备时,标准输入和标准输出流才会被完全缓冲。”

只要对stderr 的写入以'\n' 结束并且缓冲模式没有改变,那么应该可以看到输出。


看看 C++ 如何处理行尾很有趣。

What is the difference between endl and \n in C++?


@paxdiablo 很好地回答了Why does printf not flush after the call unless a newline is in the format string?

【讨论】:

  • 在现代,您在哪里找到了一个不将标准输入链接到标准输出的系统?
  • @Joshua 我的椭圆环境似乎已完全缓冲。在任何情况下,考虑到等待用户输入的大量时间,在任何交互式用户输入之前fflush(stdout); 肯定不会减慢进程。如果您寻求更深入、更好记录的案例,请考虑发布该问题。
  • @chux-ReinstateMonica “无缓冲”是指输出中的每个打印都被刷新,还是“无缓冲”意味着它从未被刷新?
  • @JackMurrow:““无缓冲”是否意味着输出中的每个打印都被刷新? - 实际上是的,即使“刷新”这个词在这种情况下没有意义,因为没有缓冲的输出,没有可以刷新的缓冲区,因为缓冲区甚至不存在。
  • @jack,不,如上所述,缓冲区满时会刷新。同样当文件被显式或隐式关闭时(例如,当程序终止时)。或者当你调用 seek 时。或者当从同一个文件请求输入时。或者图书馆认为方便的任何其他时间。
【解决方案2】:

查看您展示的确切示例,与调用 fflush 不太可能有明显差异。特别是你只写了两行数据,然后退出。正常退出程序(通过调用exit 或从main 返回)需要:

接下来,刷新所有打开的带有未写入缓冲数据的流,关闭所有打开的流,并删除所有由 tmpfile 函数创建的文件。

因此,这里唯一的可能是系统在第一次 fflush 调用之后但在您的程序退出之前立即崩溃(或按该顺序发生的事情)。

不过,我猜,真正的意图是当您打印出“输入名称:”提示时,您打算让程序停止并从用户那里读取名称:

printf("Enter name: ");
char name[256];
fgets(name, sizeof(name), stdin);

对于这种情况,通常不需要刷新输出流。

该标准特别指出:

当一个流被行缓冲时,字符应该是 当换行符被发送到主机环境或从主机环境传输 遭遇。此外,字符旨在作为块传输到主机 填充缓冲区时的环境,在无缓冲流上请求输入时,或 当在需要传输的行缓冲流上请求输入时 来自宿主环境的字符。

现在,您可以提出一点论点,即这仅讨论了预期的内容,而不是要求的内容。这基本上归结为相当简单:操作系统可以进行一些缓冲,您的程序几乎无法控制这些缓冲。因此,它基本上归结为非常简单:当您从stdin 读取时,如果有任何内容已写入stdout,但尚未刷新,它将在系统等待来自@987654328 的输入之前自动刷新@ (除非您已经完成了将一个或两个重定向到文件之类的操作)。

我有理由确定,基本上每个明确地进行刷新的论点都是纯粹的理论。理论上,可能有一个系统不会自动执行此操作,并且可能不会完全违反标准的任何严格要求。

然而,实际上,实际系统分为两类:那些是自动刷新的,因此通过显式执行不会获得任何好处,而在那些上刷新根本不会有任何好处的系统,因此您不会获得任何好处明确地这样做。

仅作为后者的一个示例,IBM 大型机的一些旧终端在您可能称之为屏幕缓冲模式下工作。基本上,您会向终端发送表单。然后,用户将数据编辑到表单中。然后,当他们填写完整个表格后,他们按下了“发送”按钮,整个表格的所有数据立即发送到 CPU。在这样的系统上,调用fflush 将不足以(几乎)使代码工作。

总结

有些系统不需要调用fflush
有些系统调用fflush 是不够的。
我有理由确定没有系统可以调用fflush 既必要又足够。

【讨论】:

  • 我看到一个太多的进度条在未连接到终端时不起作用,因为进度条最终被缓冲了。为控制台程序编写 GUI 包装器时真的很烦人。
  • 进度条是一种完全不同的情况:我们正在输出但读取输入。是的,对于这种情况,冲洗可能会有所帮助。
【解决方案3】:

不是真的,不。我在逻辑块处刷新,因此我可以或多或少地告诉程序崩溃的位置,并且没有额外的硬调试时间,因为它在缓冲区中输出崩溃。

我的想法是,我将在去执行除了产生更多输出之外的其他事情之前致电fflush()

刷新太多 = 慢代码。

在读取输入之前调用 fflush() 是无害的(唯一的问题是它是否会隐式执行),因此如果您出于某种原因这样做,则没有理由停止这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 2012-08-05
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多