【问题标题】:Why do C compilers prepend underscores to external names?为什么 C 编译器在外部名称前加上下划线?
【发布时间】:2019-06-05 14:51:38
【问题描述】:

我已经在 C 语言中工作了很长时间,以至于编译器通常会在 extern 的开头添加下划线这一事实已被理解...但是,another SO question today 让我想知道为什么添加下划线。一位wikipedia article 声称原因是:

C 编译器的常见做法是在所有外部范围程序标识符前加上前导下划线,以避免与运行时语言支持的贡献发生冲突

我认为这至少有一个道理,但它似乎并没有真正回答这个问题,因为如果将下划线添加到所有外部,它对防止冲突没有多大帮助。

有没有人对前导下划线的基本原理有很好的信息?

添加的下划线是 Unix creat() 系统调用不以“e”结尾的部分原因吗?我听说某些平台上的早期链接器名称限制为 6 个字符。如果是这样的话,那么在外部名称前加上下划线似乎是一个彻头彻尾的疯狂想法(现在我只有 5 个字符可以玩......)。

【问题讨论】:

  • 应该注意的是,这种行为在现代 ELF 系统上并不适用。显然,这在 aout/coff 平台中很常见。
  • 为什么 Clang 在 OS X 上这样做?怎么关掉?

标签: c language-history


【解决方案1】:

如果 c 编译器总是在每个符号前添加下划线, 那么启动/c-runtime代码(通常用汇编编写)可以安全地使用不以下划线开头的标签和符号(例如符号'start')。

即使您在 c 代码中编写了 start() 函数,它也会在 object/asm 输出中生成为 _start。 (请注意,在这种情况下,c 代码不可能生成不以下划线开头的符号)因此启动编码器不必担心为每个代码发明晦涩的不可能符号(如 $_dontuse42%$)他/她的全局变量/标签。

所以链接器不会抱怨名称冲突,并且程序员很高兴。 :)

以下内容与编译器在其输出格式中添加下划线的做法不同。

这种做法后来被编入 C 和 C++ 语言标准的一部分,其中前导下划线的使用保留用于实现。

这是 c 系统库和其他系统组件遵循的约定。 (以及诸如 __FILE__ 之类的东西)。

(请注意,这样的符号(例如:_time)可能会在生成的输出中导致两个前导下划线(__time))

【讨论】:

    【解决方案2】:

    C 编译器的常见做法是在所有外部范围程序标识符前加上前导下划线,以避免与运行时语言支持的贡献发生冲突

    如果编译器提供运行时支持,您会认为在运行时支持中为少数外部标识符添加下划线会更有意义!

    当 C 编译器第一次出现时,在这些平台上用 C 编程的基本替代方案是用汇编语言编程,将用汇编器和 C 编写的目标文件链接在一起是(并且偶尔仍然是)有用的。所以真的(恕我直言) ) 添加到外部 C 标识符的前导下划线是为了避免与您自己的汇编代码中的标识符发生冲突。

    (另见GCC's asm label extension;请注意,这个前置下划线可以被视为name mangling的简单形式。更复杂的语言(如C++)使用更复杂的name mangling,但这就是它开始的地方.)

    【讨论】:

    • 我喜欢讽刺的“GCC 还没有能力将静态变量存储在寄存器中。也许会添加。”在链接的文档中发表评论。
    • @MichaelBurr:这可能不是讽刺。在某些系统上,您可以保留一个全局寄存器作为指向某个内存区域的指针(例如,R9 在 ARM EABI 的某些变体中用于静态基指针)。
    • 在 C 名称前面加上 _ 的另一个原因是确保它们不会与寄存器名称冲突。例如static int eax, r0; 在 C 中是合法的,并且某些汇编语法不会用任何东西装饰寄存器名称。
    【解决方案3】:

    主函数不是可执行文件的真正入口点。一些静态链接文件具有最终调用 main 的真正入口点,而这些静态链接文件拥有不以下划线开头的命名空间。在我的系统上,在 /usr/lib 中,有 gcrt1.o、crt1.o 和 dylib1.o 等。其中每一个都有一个没有下划线的“start”函数,最终将调用“_main”入口点。除了这些文件之外的所有其他内容都具有外部范围。历史与在一个项目中混合汇编程序和 C 语言有关,其中所有 C 语言都被认为是外部的。

    【讨论】:

      【解决方案4】:

      来自Wikipedia

      C 编译器的常见做法是在所有外部范围程序标识符前加上前导下划线,以避免与运行时语言支持的贡献发生冲突。此外,当 C/C++ 编译器需要在翻译过程中将名称引入外部链接时,这些名称通常用多个前导或尾随下划线的组合来区分。

      这种做法后来被编入 C 和 C++ 语言标准的一部分,其中使用前导下划线保留用于实现。

      【讨论】:

        【解决方案5】:

        据我所知,这是为了避免命名冲突。不适用于其他外部变量,但更多的是这样当您使用库时,它希望不会与用户代码变量名称冲突。

        【讨论】:

          猜你喜欢
          • 2011-08-19
          • 2020-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-28
          相关资源
          最近更新 更多