为什么C编译器将下划线添加到外部名称?

时间:2010-04-13 06:45:26

标签: c language-history

我已经在C工作了很长时间以至于编译器通常在extern的开头添加下划线这一事实才被理解......然而,another SO question today让我想知道添加下划线的真正原因。 wikipedia article声称原因是:

  

C编译器通常会在所有外部作用域程序标识符前面加上一个前导下划线,以避免与运行时语言支持的贡献发生冲突

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

有没有人对领先的下划线的理由有充分的了解?

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

5 个答案:

答案 0 :(得分:17)

  

C编译器通常会在所有外部作用域程序标识符前面加上一个前导下划线,以避免与运行时语言支持的贡献发生冲突

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

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

(另请参阅GCC's asm label extension;请注意,此前置下划线可以被视为 name mangling 的简单形式。更复杂的语言(如C ++)使用更复杂的名称修改,但这是它开始了。)

答案 1 :(得分:5)

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

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

因此链接器不会抱怨名称冲突,程序员很高兴。 :)

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

  

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

对于c系统库和其他系统组件,

遵循惯例。 (以及诸如__FILE__之类的东西)。

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

答案 2 :(得分:3)

从我一直听到的是避免命名冲突。不适用于其他外部变量,但更多是因为当您使用库时,它将不会与用户代码变量名冲突。

答案 3 :(得分:3)

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

答案 4 :(得分:2)

来自Wikipedia

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

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