为什么GCC中的C预处理器将单词linux
(小写字母)解释为常量1
?
测试.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
$ gcc -E test.c
的结果(在预处理阶段后停止):
....
int main(void)
{
int 1 = 5;
return 0;
}
这当然会产生一个错误。
(BTW:stdio.h
文件中没有#define linux
。)
5条答案
按热度按时间fcy6dtqo1#
在过去(pre-ANSI)中,预定义诸如
unix
和vax
这样的符号是一种允许代码在编译时检测它是为哪个系统编译的方法。(超出了第一版K&R后面的参考资料),和C代码通常是#ifdef
的复杂迷宫,以允许系统之间的差异。这些宏定义通常由编译器自己设置,由于没有关于哪些标识符可以被实现使用以及哪些标识符保留给程序员的真实的规则,编译器编写者可以随意使用像unix
这样的简单名称,并假设程序员会简单地避免使用这些名称来实现自己的目的。1989年ANSI C标准引入了一些规则,限制了实现可以合法预定义的符号。编译器预定义的宏只能有一个以两个下划线开头的名称,或者一个下划线后跟一个大写字母,这样程序员就可以自由地使用不匹配该模式的标识符,也可以使用标准库中未使用的标识符。
因此,任何预定义
unix
或linux
的编译器都是不相容的,因为它将无法编译使用类似int linux = 5;
的代码的完全法律的代码。碰巧的是,gcc在默认情况下是不符合的--但是可以使它符合正确的命令行选项(相当好):
有关详细信息,请参阅the gcc manual。
gcc将在未来的版本中逐步淘汰这些定义,所以你不应该编写依赖于它们的代码。如果你的程序需要知道它是否是为Linux目标编译的,它可以检查是否定义了
__linux__
(假设你使用的是gcc或与之兼容的编译器)。请参见the GNU C preprocessor manual了解更多信息。一个基本上无关紧要的旁白:1987年International Obfuscated C Code Contest的“最佳单行程序”赢家大卫Korn(是的,Korn Shell的作者)利用了预定义的
unix
宏:它打印
"unix"
,但原因与宏名称的拼写完全无关。我不想在这里发布任何剧透,我鼓励任何阅读本文的人先自己尝试理解代码。但如果你真的想给予,我在这里发布了一个解释:https://gist.github.com/Keith-S-Thompson/6920347
k5ifujac2#
这似乎是一个(未公开)“GNU扩展”:[* 更正 *:我终于在文档中找到了一个提到。见下文。]
以下命令使用
-dM
选项打印所有预处理器定义;因为输入“file”是空的,所以它显示的是预定义的宏。它是在标准的ubuntu安装上用gcc-4.7.3运行的。你可以看到预处理器是标准感知的。总共有243个-std=gnu99
宏和240个-std=c99
宏;我过滤了相关的输出。“gnu标准”版本也是
#define unix
。(使用c11
和gnu11
会产生相同的结果。)我想他们有他们的理由,但是在我看来,这使得gcc的默认安装(除非另有说明,否则它使用
-std=gnu89
编译C代码)不符合规范,而且--正如这个问题中所示--令人惊讶。在符合规范的实现中,不允许使用名称不以下划线开始的宏来污染全局名称空间。(6.8.10p2:“任何其他预定义的宏名称应以前导下划线开始,后跟一个大写字母或第二个下划线”,但是,如附录J.5(可移植性问题)中所述,此类名称通常是预定义的。)当我最初写这个答案的时候,我在gcc中找不到任何关于这个问题的文档,但是我最终发现了这个问题,不是在C实现定义的行为中,也不是在C扩展中,而是在
cpp
手册的3.7.3部分中,其中指出:我们正在逐步淘汰所有预定义的宏,这些宏在保留的命名空间之外。你永远不应该在新的程序中使用它们...
2jcobegt3#
因为
linux
是编译器在Linux上运行或为Linux编译(如果是交叉编译器)时定义的内置宏。有很多这样的预定义宏。在GCC中,你可以用途:
得到一个宏列表。(我还没有设法说服GCC直接接受
/dev/null
,但空文件似乎可以正常工作。)在MacOSX 10.8.5上运行GCC 4.8.1时,我得到了输出:这是一个空文件中的236个宏,当我将
#include <stdio.h>
添加到该文件中时,定义的宏数量增加到505个,其中包括各种平台标识宏。puruo6ea4#
从
info gcc
(着重号为mine):-ansi
个在C模式下,它等效于
-std=c90
。在C模式下,它等效于-std=c++98
。这将关闭GCC中与ISO C90不兼容的某些功能(编译C代码时),或标准C(编译C代码时),如asm
和typeof
关键字、和预定义的宏,如“unix”和“vax”,用于标识您所使用的系统类型。它还启用了不受欢迎且很少使用的ISO三字母特征。对于C编译器,它禁用对C样式//
注解以及inline
关键字的识别。(It在示例中使用vax而不是linux,因为在编写时它可能更受欢迎;- ).
基本思想是,GCC只在使用
-ansi
选项调用时才尝试完全符合ISO标准。yvfmudvl5#
使用此命令
来得到这个