当前位置: 首页 > 面试题库 >

为什么C预处理程序将单词“ linux”解释为常量“ 1”?

赫连俊雄
2023-03-14
问题内容

为什么 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;
}

当然哪个会产生错误。

(顺便说一句:没有#define linuxstdio.h的文件。)


问题答案:

在过去(ANSI之前的版本)中,预定义符号(例如unix和)vax是一种允许代码在编译时检测其针对哪个系统进行编译的方法。当时没有正式的语言标准(除了K&R第一版后面的参考资料之外),任何复杂的C代码通常都是#ifdefs
的复杂迷宫,以允许系统之间的差异。这些宏定义通常由编译器本身设置,而不是在库头文件中定义。由于对于实现可以使用哪些标识符以及哪些标识符保留给程序员没有真正的规则,因此编译器作者可以随意使用简单的名称,例如,unix并假定程序员会简单地避免出于自己的目的使用这些名称。

1989年的ANSI
C标准引入了一些规则,这些规则限制了实现可以合法定义的符号。编译器预定义的宏只能使用以两个下划线开头的名称,或者以下划线后接大写字母的名称,从而使程序员可以自由使用与该模式不匹配且未在标准库中使用的标识符。

结果,任何预定义unixlinux不合格的编译器都将无法编译完全合法的代码,例如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",但是由于与宏名称的拼写绝对无关的原因。



 类似资料:
  • 这当然会产生错误。 (顺便说一句:在文件中没有。)

  • 为什么GCC中的C预处理器将单词(小写字母)解释为常量? 测试C: 的结果(预处理阶段后停止): 这当然会产生一个错误。 (顺便提一下:文件中没有。)

  • 我有一个简单的批处理测试文件test.bat如下行: 当我运行它时,我希望得到的是测试,而不是: 为什么batch会试图解释这条评论?或者这里发生了什么?如果我去掉注释,脚本会按预期打印测试。 文件中也没有提到这一点。

  • 本文向大家介绍常用C/C++预处理指令详解,包括了常用C/C++预处理指令详解的使用技巧和注意事项,需要的朋友参考一下   预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。预处理命令以符号“#”开头。   常用的预处理指令包括: 宏定义:#define 文件包含:#include 条件编译:#if、#elif、#ifndef、#ifdef、#endif、#undef

  • 问了这个问题后,我很困惑,于是决定为一个C编译器程序构建类似的测试。这是我的代码: 使用选项在GCC下编译 测试代码被更改,这样错误值只能在运行时知道(而不能在编译时知道),这样GCC优化器就不能删除循环的代码。 我们应该期待CPU的加速吗?(正如GCC编译程序预测的那样)

  • 问题内容: 为什么将Java常量声明为static? 在此我了解使用final吗?购买为什么它必须是静态的?为什么它应该是类变量,而不是实例变量? 问题答案: 如果它可以随类的实例而变化,那么显然它不是 常数 。为的每个实例获得不同的pi值意味着什么(甚至不允许构造实例)?还是每个实例的大小写不区分大小写?