【问题标题】:What is the reason for having unreserved identifiers as built-in macros in gcc?在 gcc 中将未保留的标识符作为内置宏的原因是什么?
【发布时间】:2012-01-30 10:24:57
【问题描述】:

今天我偶然发现了一个相当有趣的编译器错误:

int main() {
  int const unix = 0; // error-line
  return unix;
}

在 gcc 4.3.2 中给出以下信息(是的,古老的...):

error: expected unqualified-id before numeric constant

这绝对令人困惑。

幸运的是,clang (3.0) 更有帮助(和往常一样):

error: expected unqualified-id
  int const unix = 0
            ^
<built-in>:127:14: note: expanded from:
#define unix 1
             ^

我当然没想到unix,既不是大写也不是下划线开头的宏,尤其是内置宏。

我检查了 gcc 中的预定义宏,有 2 个(在我的平台上)使用“未保留”符号:

$ g++ -E -dM - < /dev/null | grep -v _
#define unix 1
#define linux 1

所有其他都是带有前导下划线的“行为良好”的宏,使用传统的保留标识符,示例:

#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1

#define __unix__ 1
#define __unix 1

#define __CHAR_BIT__ 8
#define __x86_64 1
#define __amd64 1
#define _LP64 1

(乱七八糟,好像没有什么特别的顺序……)

此外,还有很多“相似”的符号,所以我想存在向后兼容性的问题......

那么,unixlinux 宏从何而来?

【问题讨论】:

  • 在 gcc 4.7 中工作正常,似乎是一些错误 :)
  • 我添加了历史标签,我相信它至少是历史的。
  • 很高兴知道。通常我看到相反的情况,就像程序员无缘无故(可能是对被禁止的幼稚热情除外)使用像 __MY_INCLUDE_FILE__ 这样的名字而不是合法的名字来包含警卫。
  • @6502:我不认为这是对禁忌的热情,我认为这是对货物的狂热编程。人们在编译器(或 SDK)提供的头文件中看到了用于包含保护的标识符,并开始这样做。我认为早期的 Windows SDK 对此做出了很大贡献。 MS 一直在清理 Windows SDK 中的这类东西,但早期的损害已经存在了一段时间(我猜其中很多早于 C 标准)。
  • @fge:标识符任何部分的双下划线保留给编译器实现者。开头的保留单下划线后跟大写字母(任何使用)或开头的单下划线后跟小写字母(用于全局标识符)。通过使用禁止的名称,您可能会遇到非常难以调试的问题(例如,您的程序在 main 启动之前就崩溃了,这仅仅是因为您决定命名一个全局变量 _init)。只需在开头避免下划线(它们也很丑陋)并使用例如MYFILE_H_INCLUDED

标签: c++ linux unix macros history


【解决方案1】:

gcc 默认不完全符合任何 C 标准。

使用-ansi-std=c99-std=c1x 调用它,unix 不会被预定义。 (-std=c1x 可能会变成future 最近的 gcc 版本中变成 -std=c11。)

这有点令人困惑,这是在 GNU 预处理器的单独手册中记录的,而不是在 gcc 手册中。

引用 GNU 预处理器文档(info cpp,4.5 版):

C 标准要求所有特定于系统的宏都是 “保留的命名空间”。所有以两个下划线开头的名称, 或下划线和大写字母,为编译器保留 和图书馆随心所欲地使用。然而,历史上 系统特定宏的名称没有特殊前缀;为了 例如,在 Unix 系统上定义“unix”是很常见的。为了 所有这些宏,GCC 提供了一个带有两个下划线的并行宏 在开头和结尾添加。如果定义了“unix”, `__unix__' 也会被定义。永远不会超过两个 下划线; `_mips' 的平行线是 `__mips__'。

当 `-ansi' 选项,或任何要求严格的 `-std' 选项时 一致性,提供给编译器,所有系统特定的 保留命名空间之外的预定义宏被抑制。这 保留命名空间内的并行宏保持定义。

我们正在逐步淘汰所有超出 保留的命名空间。你不应该在新程序中使用它们,我们 鼓励您更正旧代码以使用并行宏 每当你找到它。我们不建议您使用系统特定的 保留命名空间中的宏。最好在 从长远来看,使用工具专门检查您需要的功能 比如`autoconf'。

手册的当前版本是here

【讨论】:

  • 酷,所以它确实具有历史意义!我有预知能力! :) +1
  • @Xeo:事后预知?现在这是我想拥有的超能力!
  • 为什么?我很久以前在问题中添加了标签。 :P
  • @KeithThompson:非常感谢。现在我不仅知道他们正在努力删除它们,而且我还知道在需要时如何将它们踢出去。您是否也知道为什么首先介绍它们?
  • @MatthieuM.:我相信 unix 宏是在 ANSI 标准化语言并确定保留给实现的标识符之前很久就引入的。由于这样的事情,ANSI C 之前的 C 通常需要“#ifdefs 的令人毛骨悚然的迷宫”。 1987 年的“Best One Liner”获胜者IOCCC 充分利用了它:main() { printf(&amp;unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
猜你喜欢
  • 2016-11-16
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多