为什么 stderr、stdin、stdout 定义为宏?

Jon*_* S. 5 c c99 language-lawyer

C99 将这三个定义为宏,它们“是 \xe2\x80\x98\xe2\x80\x98 类型的表达式,指向 FILE\xe2\x80\x99\xe2\x80\x99,分别指向与标准错误、输入和输出流”。

\n

鉴于此,这些表达式可能会或可能不会const(即指针可能会或可能不会被写入以重定向到另一个文件或流);它们可能具有内部或外部链接(即,重定向到另一个文件或流的写入可能不会在两个单独编译的 C 文件之间持续存在)。因此,尝试stdout从 C 文件内重定向可能会出现意外行为。

\n

(注意:在 gcc-9.4.0 中,它们似乎是映射到const具有相同名称的外部链接的非变量的宏,并附有注释:/* C89/C99 say they're macros. Make them happy. */。)

\n

任何人都可以阐明是否有官方原因,或者是否可以安全地假设这是原始标准中的另一个“疏忽”,并且应该避免在 C 代码中重定向流以保持可移植性?

\n

Joh*_*ger 9

这些表达式可能是也可能不是const(即指针可能会也可能不会被写入重定向到另一个文件或流);

正确的。

并且它们可能具有内部或外部链接

宏标识符本身没有链接,只有当它们的替换文本是标识符时,链接才对它们有意义。

[...] 因此,尝试stdout从 C 文件内重定向可能会出现意外行为。

不可以。 尝试分配新值stdout可能会失败或产生意外结果。但是,是的,您已经发现执行此类分配并不是更改stdout连接目的地的可靠方法。

谁能解释一下这是否有官方原因

C99有一个官方的基本原理文档。它没有解决这一点。

或者可以安全地假设这是原始标准中的另一个“疏忽”

我不知道你的意思。我认为可以很安全地假设stdin, stdout, 和stderr被有意指定为宏。它直接从他们的规范中得出,无论出于任何目的,您都不能依赖于分配给他们。

我推断它们的指定是为了允许实现的灵活性。例如,它们可以作为函数调用或表查找来实现。它们不必是对象标识符。

为了保持可移植性,应该避免在 C 代码中重定向流?

不可以 。为了 严格遵守 C 语言规范(大致相当于“保持可移植性”),必须避免分配给 、 、 和。如果您想将其中一个标准流与不同的目标关联,那么这就是该函数的主要目的,在标准 C 的所有版本中都可用。stdinstdoutstderrfreopen()


Ste*_*mit 7

之所以会这样,是有历史原因的。

\n

您不能重新分配stdinstdoutstderr。如果您需要重新打开这些流,以便它们在其他地方执行 I/O,那么这就是目的freopen

\n

在最早的实现中<stdio.h>,有一个全局数组

\n
FILE _iobf[20];\n
Run Code Online (Sandbox Code Playgroud)\n

然后stdinstdout、 和stderr是,是的,宏:

\n
#define stdin &_iobf[0]\n#define stdout &_iobf[1]\n#define stderr &_iobf[2]\n
Run Code Online (Sandbox Code Playgroud)\n

这工作得很好,而且很漂亮而且紧凑。今天在我们看来“很奇怪” \xe2\x80\x94 “为什么有人会这样写?”,你可能会问 \xe2\x80\x94 但重点是他们确实是这样写的,而且它这种情况持续了很多年,时间长到 C 标准必须通过禁止符合标准的程序为这些“变量”分配新值来适应它。

\n

(再多说一点“为什么有人会那样写?”,我相信答案是,经济。C被<stdio.h>发明的时候,C是全新的,PDP-11有64k地址空间,你甚至不能总是使用全部。每个人都使用read()和进行原始 I/O write(),或者实现自己的专用缓冲方案。有些人问,“为什么我们必须实现自己的?为什么没有一个标准吗?”,而其他人则说,“每个人的需求都明显不同;没有一种实现可以满足每个人。”这很像我们今天仍在讨论的关于为什么 C 没有一个标准链接的讨论。 -list 实现。在某个时刻,Mike Lesk 宣称他实现一个标准 I/O 库,并且有可能提出一种能够满足每个人的需求或足够好的实现。但是为了安抚反对者,为了不给已经达到 64k 上限的大型程序带来不便,我们有很大的动机将代码和数据的内存占用 \xe2\x80\x94 保持在最小限度。我不记得所有的细节,但该FILE _iobf[20];方案确实最终变得非常紧凑,我猜您可能会想出任何“明显更好”的方案 \xe2\x80\x94 ,其中涉及动态分配 FILE 结构而不是拥有 20 个 FILE 结构的固定数组 \xe2\x80\x94 会占用更大的内存。)

\n