在旧时代(ANSI 之前),预定义符号(如unix
和vax
)是一种允许代码在编译时检测它正在编译的系统的方法。当时没有官方语言标准(除了 K&R 第一版后面的参考资料),任何复杂的 C 代码通常都是一个复杂的#ifdef
s 迷宫,以允许系统之间的差异。这些宏定义通常由编译器自己设置,而不是在库头文件中定义。由于没有关于实现可以使用哪些标识符以及为程序员保留哪些标识符的真正规则,编译器编写者可以随意使用简单的名称,unix
并假设程序员会简单地避免将这些名称用于自己的目的。
1989 年的 ANSI C 标准引入了限制实现可以合法预定义的符号的规则。编译器预定义的宏名称只能以两个下划线开头,或者下划线后跟一个大写字母,从而使程序员可以自由使用与该模式不匹配且未在标准库中使用的标识符。
因此,任何预定义unix
或linux
不符合标准的编译器都将无法编译使用类似int linux = 5;
.
碰巧的是,默认情况下 gcc 是不符合标准的——但可以使用正确的命令行选项使其符合(相当好):
gcc -std=c90 -pedantic ... # or -std=c89 or -ansi
gcc -std=c99 -pedantic
gcc -std=c11 -pedantic
有关详细信息,请参阅gcc 手册。
gcc 将在未来的版本中逐步淘汰这些定义,因此您不应该编写依赖于它们的代码。如果您的程序需要知道它是否正在为 Linux 目标编译,它可以检查是否__linux__
已定义(假设您使用的是 gcc 或与其兼容的编译器)。有关更多信息,请参阅GNU C 预处理器手册。
一个基本上无关紧要的问题:David Korn(是的,Korn Shell 的作者)在 1987 年国际混淆 C 代码竞赛unix
中的“最佳单行代码”获胜者利用了预定义的宏:
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
它打印"unix"
,但原因与宏名称的拼写完全无关。