为什么在写入stderr之前需要使用fflush清空stdout缓冲区?

15

我正在阅读《UNIX网络编程:套接字API》这本书,并且在示例代码中,他们有一个包含以下行的错误处理函数:

fflush(stdout);     /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);

buf包含错误描述。我不理解为什么在第一行要在stdout上使用fflush,以及为什么注释解释了它的用途。

2个回答

26
这是因为缓冲区的原因。标准输出和标准错误通常使用不同的缓冲方式。标准输出通常是行缓冲的,这意味着它不会显示输出,直到看到一个换行符。标准错误通常是无缓冲的,并且会立即打印,因为你应该尽快看到错误消息。
但它们都去了同一个地方,终端。这就是“/*如果stdout和stderr相同*/”的含义。它们通常是相同的。但由于它们的缓冲方式不同,这可能导致它们以不同的顺序显示。
考虑一下这段代码。请注意缺少换行符。
#include <stdio.h>

int main() {
    fprintf(stdout, "This is to stdout. ");
    fprintf(stderr, "This is to stderr. ");
    fprintf(stdout, "This is also to stdout. ");
}

你会期望输出结果是:

This is to stdout. This is to stderr. This is also to stdout.

但事实并非如此。它是无序的。
$ ./test
This is to stderr. This is to stdout. This is also to stdout.

标准错误输出会立即显示,它是无缓冲的。而标准输出必须等待输出缓冲区被换行符刷新后才能显示。由于没有换行符,所以只有当程序退出时才会刷新。
在使用标准错误输出之前刷新标准输出,可以确保输出按照正确的顺序显示,而不受缓冲影响。
#include <stdio.h>
#include <unistd.h>

int main() {
    fprintf(stdout, "This is to stdout. ");
    fflush(stdout);
    fprintf(stderr, "This is to stderr. ");
    fprintf(stdout, "This is also to stdout. ");
}

$ ./test
This is to stdout. This is to stderr. This is also to stdout. 

这样可以确保错误消息与普通消息按正确顺序输出。这避免了对程序的哪个部分应用了哪个错误消息的混淆。

你的第二个例子是错误的;This is also to stdout.This is to stdout. 应该交换。 - Qix - MONICA WAS MISTREATED
如果stderr不寻常地是_line_缓冲的,那么类似的问题会发生,而这个答案无法解决。严谨地说,在写入之前刷新另一个。或者使用良好的礼仪,在完成后[刷新一次]。 - chux - Reinstate Monica
1
@Qix 谢谢。我最初在代码中混淆了文件句柄。 - Schwern

2
如果标准输出和标准错误都指向同一文件,您必须确保先写入stdout缓冲区中的任何内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接