我有几个旧的代码库来初始化变量(能够随意重定向输入/输出),如
FILE *usrin = stdin, *usrout = stdout;
然而,gcc(8.1.1,Fedora生牛皮上的glibc 2.27.9000)在这个(initializer is not a compile time constant
)bal。我在/usr/include/stdio.h
中看到:
extern FILE *stdin;
/* C89/C99 say they're macros. Make them happy */
#define stdin stdin
首先,对我来说没有意义,你不能以这种(相当自然的)方式初始化变量。当然,你可以在以后的代码中完成它,但这是一个麻烦。
第二,为什么宏观扩张不是一个常数?
第三,让它们成为宏的理由是什么?
答案 0 :(得分:6)
首先,对我来说没有意义,你可以用这种(非常自然的)方式初始化变量。
第二,为什么宏观扩张不是一个常数?
import errno
import sh
cmd = sh.Command('./hello.sh')
for line in cmd(_iter=True, _iter_noblock=True):
if line == errno.EWOULDBLOCK:
pass
else:
print(line)
,stdin
和stdout
是在C库启动期间初始化的指针,可能是内存分配的结果。它们的值在编译时是不可知的 - 取决于C库的工作方式,它们甚至可能不是常量。 (例如,如果他们指向静态分配的结构,他们的值将受到ASLR的影响。)
第三,让它们成为宏的理由是什么?
它保证stderr
为真。这可能是为了与一些需要处理缺乏stdio支持的系统的旧程序兼容而添加的。
答案 1 :(得分:3)
经典地,stdin
,stdout
和stderr
的值为variations on the theme of:
#define stdin (&__iob[0])
#define stdout (&__iob[1])
#define stderr (&__iob[2])
这些是地址常量,可以在文件范围的变量的初始值设定项中使用:
static FILE *def_out = stdout;
但是,C标准并不保证值是可以像C11 §7.21 Input/output <stdio.h.>
那样使用的地址常量:
stderr
,stdin
,stdout
它是“指向FILE的指针”类型的表达式,它们分别指向与标准错误,输入和输出流关联的FILE对象。
十年或更久以前的某个时候,GNU C库更改了它们的定义,因此您无法再使用stdin
,stdout
或stderr
作为文件范围变量的初始化程序,或者具有函数范围的static
变量(尽管您可以使用它们来初始化函数中的自动变量)。因此,在许多系统上运行了多年的旧代码已停止在Linux上运行。
stdin
等的宏扩展可以是简单的身份扩展(#define stdin stdin
)或等效的(在macOS上,#define stdout __stdoutp
)。这些是变量,而不是地址常量,因此您无法在文件范围初始值设定项中复制变量的值。这是令人讨厌的,但标准并没有说它们是地址常数,所以它是合法的。
它们必须是宏,因为它们总是宏,所以它保留了与标准I / O库的曙光(大约1978年,早在有标准C库本身之前)的向后兼容性。 / p>