stdout和STDOUT_FILENO之间有什么区别?

85

我想知道在Linux C中,stdoutSTDOUT_FILENO的区别。

经过一番搜索,我得出了以下结论。请帮我审核并纠正其中的任何错误。谢谢。

  • stdout属于C语言标准I/O流,其类型为FILE*,定义在stdio.h中。

  • STDOUT_FILENO具有int类型,在unistd.h中被定义。它是LINUX系统的文件描述符。在unistd.h中,它被解释如下:

The following symbolic constants shall be defined for file streams:

STDERR_FILENO
    File number of stderr; 2.
STDIN_FILENO
    File number of stdin; 0.
STDOUT_FILENO
    File number of stdout; 1.

据我看来,STDOUT_FILENO 属于系统级调用,某种程度上,它类似于系统 API,可以用于描述系统中的任意设备。

stdout 位于更高级别(用户级别?),实际上封装了 STDOUT_FILENO 的细节。其中 stdout 具有 I/O 缓冲区。

这就是我对它们之间差异的理解。欢迎任何评论或纠正,感谢。


@KerrekSB 这个问题是我不确定我的理解是否正确? - Bing Lu
STDOUT_FILENO 不仅仅是Linux专用的,所有符合POSIX标准(包括Unix)的系统都有这个命令(例如,MacOSX、FreeBSD或Solaris)。 - Basile Starynkevitch
1个回答

110

stdout 是一个 FILE* 指针,表示标准输出流。因此,fprintf(stdout, "x=%d\n", x); 显然与 printf("x=%d\n", x); 有相同的行为;您可以使用 stdout 来调用 <stdio.h> 函数,例如 fprintf()fputs() 等。

STDOUT_FILENO 是整数文件描述符(实际上是整数1)。您可能会将其用于 write 系统调用。

两者之间的关系是 STDOUT_FILENO == fileno(stdout)

(除非您做了奇怪的事情,比如 fclose(stdout);,或者在 fclose(stdin) 之后进行一些 freopen,这几乎永远不应该做!请参见 此处,由 J.F.Sebastian 评论)

通常喜欢使用 FILE*,因为它们具有缓冲功能(因此通常表现良好)。有时,您可能想调用 fflush() 来刷新缓冲区。

您可以使用文件描述符号来进行 系统调用,例如 write()(由 stdio 库使用),或者 poll()。但是使用系统调用很笨重。它可能会给您非常好的效率(但这很难编写),但通常 stdio 库已经足够好了(而且更具可移植性)。

当然,你应该包含#include <stdio.h>来使用stdio函数,还需包含#include <unistd.h>和其他一些头文件来使用类似write的系统调用。而且,stdio函数是通过系统调用实现的,因此fprintf()可能会调用write()


26
"STDOUT_FILENO == fileno(stdout)" 这个表达式非常好,帮助我完全理解了这个问题。非常感谢。@Basile - Bing Lu
2
STDOUT_FILENO == fileno(stdout) -- 这个表达式总是成立吗? - jfs
2
文件描述符还可以通过使用 dup2 将流重定向到文件描述符表中的不同位置,以便在进行系统调用并将输出打印到 stdout 的子进程的输出被重定向到文本文件之前告诉操作系统。 - Jake Levi
我对 STDOUT_FILENO == fileno(stdout) 感到困惑,除非你做一些奇怪的事情,比如这里。在我看来,既然 STDOUT_FILENO != fileno(stdout) 是可能的,那么在 API 中使用 fileno(stdout) 不是更好吗?除了与 fileno(stdout) 进行比较时,你实际上什么时候需要使用 STDOUT_FILENO - Rich Jahn
@Rich,这在没有使用<stdio.h>的程序中非常有用。就在刚才,我在设置子进程重定向时发现它非常有用,例如确保其输入和输出流连接到我刚刚为其创建的管道。 - Toby Speight

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