为什么GCC中的C预处理器将单词 linux
(小写字母)解释为常量 1
?
test.c的:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
$ gcc -E test.c
的结果(在预处理阶段后停止):
....
int main(void)
{
int 1 = 5;
return 0;
}
哪个 - 当然 - 会产生错误 .
(顺便说一句:stdio.h文件中没有 #define linux
. )
5 回答
这似乎是一个(无证件)"GNU extension":[更正:我终于在文档中提到了一个提及 . 见下文 . ]
以下命令使用
-dM
选项打印所有预处理器定义;由于输入"file"为空,它显示预定义的宏 . 它在标准的ubuntu安装上使用gcc-4.7.3运行 . 您可以看到预处理器是标准感知的 . 总共有243个宏-std=gnu99
和240个-std=c99
;我过滤了输出的相关性 ."gnu standard"版本也
#define unix
. (使用c11
和gnu11
会产生相同的结果 . )我想他们有他们的理由,但在我看来,gcc的默认安装(除非另有说明,否则编译C代码
-std=gnu89
)不符合,并且 - 正如在这个问题中 - 令人惊讶 . 在一致的实现中,不允许使用名称不以下划线开头的宏来粉碎全局命名空间 . (6.8.10p2:"Any other predefined macro names shall begin with a leading underscore followed by an uppercase letter or a second underscore,"但是,如附录J.5(可移植性问题)中所述,这些名称通常是预定义的 . )当我最初写这个答案时,我无法在gcc中找到关于这个问题的任何文档,但我终于发现了它,不是在C implementation-defined behaviour中,也不是在C extensions中,而是在
cpp
手册部分3.7.3中,它注意到:使用此命令
得到这个
在旧时代(ANSI之前),预定义符号(如
unix
和vax
)是一种允许代码在编译时检测正在编译的系统的方法 . 当时没有官方语言标准(超出第一版K&R背面的参考资料),任何复杂性的C代码通常是#ifdef
s的复杂迷宫,以允许系统之间的差异 . 这些宏定义通常由编译器本身设置,而不是在库头文件中定义 . 由于没有关于实现可以使用哪些标识符以及为程序员保留哪些标识符的实际规则,编译器编写者可以随意使用简单的名称,如unix
,并假设程序员只是为了自己的目的而避免使用这些名称 .1989 ANSI C标准引入了限制实现可以合法预定义的符号的规则 . 编译器预定义的宏只能有一个以两个下划线开头的名称,或者下划线后跟一个大写字母,这样程序员就可以自由地使用与该模式不匹配但未在标准库中使用的标识符 .
因此,任何预定义
unix
或linux
的编译器都是不符合的,因为它无法编译使用int linux = 5;
之类的完全合法的代码 .碰巧的是,默认情况下gcc是不符合的 - 但是它可以与正确的命令行选项一致(合理地):
有关详细信息,请参阅the gcc manual .
gcc将在未来的版本中逐步淘汰这些定义,所以你不应该为Linux目标编译't write code that depends on them. If your program needs to know whether it'它是否可以检查
__linux__
是否被定义(假设你're using gcc or a compiler that'与它兼容) . 有关更多信息,请参见the GNU C preprocessor manual .一个很大程度上无关紧要的东西:1987年"Best One Liner"的获胜者,David Korn(是的,Korn Shell的作者)利用了预定义的
unix
宏:它打印
"unix"
,但原因与宏名称的拼写完全无关 .从
info gcc
(强调我的):(它在示例中使用 vax 而不是 linux 因为当它被写入时可能它更受欢迎;-) .
基本思想是,当使用
-ansi
选项调用GCC时,GCC仅尝试完全符合ISO标准 .因为
linux
是在编译器运行或编译时定义的内置宏(如果它是交叉编译器),Linux .有很多这样的预定义宏 . 同GCC,您可以使用:
获取宏列表 . (我没有设法说服GCC直接接受
/dev/null
,但空文件似乎工作正常 . )在Mac OS X 10.8.5上运行GCC 4.8.1,我得到了输出:那是来自空文件的236个宏 . 当我将
#include <stdio.h>
添加到文件中时,定义的宏数达到505.这些宏包括各种平台识别宏 .