40

我已经在 C 领域工作了很长时间,以至于编译器通常在 an 的开头添加下划线这一事实才被extern理解......但是,今天的另一个 SO 问题让我想知道添加下划线的真正原因。维基百科文章声称原因是:

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

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

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

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

4

5 回答 5

22

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

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

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

(另请参阅GCC 的asm标签扩展;并注意,这个前置下划线可以被认为是名称修饰的一种简单形式。更复杂的语言(如 C++)使用更复杂的名称修饰,但这就是它开始的地方。)

于 2010-04-13T09:24:08.123 回答
6

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

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

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

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

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

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

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

于 2010-04-13T18:46:11.373 回答
3

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

于 2010-04-13T06:47:35.177 回答
3

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

于 2010-04-13T07:27:26.640 回答
2

来自维基百科

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

这种做法后来被编入 C 和 C++ 语言标准的一部分,其中前导下划线的使用保留用于实现。
于 2010-04-13T06:54:20.297 回答