函数名称由编译器以下划线为前缀的原因是什么?

37 c assembly naming compilation function

当我看到C应用程序的汇编代码时,如下所示:

emacs hello.c
clang -S -O hello.c -o hello.s
cat hello.s
Run Code Online (Sandbox Code Playgroud)

函数名称以下划线为前缀(例如callq _printf).为什么要这样做,它有什么优势?


例:

你好ç

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main() {
  char *myString = malloc(strlen("Hello, World!") + 1);
  memcpy(myString, "Hello, World!", strlen("Hello, World!") + 1);
  printf("%s", myString);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

hello.s

_main:                       ; Here
Leh_func_begin0:
    pushq   %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    movl    $14, %edi
    callq   _malloc          ; Here
    movabsq $6278066737626506568, %rcx
    movq    %rcx, (%rax)
    movw    $33, 12(%rax)
    movl    $1684828783, 8(%rax)
    leaq    L_.str1(%rip), %rdi
    movq    %rax, %rsi
    xorb    %al, %al
    callq   _printf          ; Here
    xorl    %eax, %eax
    popq    %rbp
    ret
Leh_func_end0:
Run Code Online (Sandbox Code Playgroud)

Jon*_*rdy 29

连接器和装载器:

在大约1974年用C语言重写UNIX的时候,它的作者已经有了广泛的assember语言库,并且更容易破坏新的C和C兼容代码的名称,而不是返回并修复所有现有代码.现在,20年后,汇编程序代码全部被重写了五次,而UNIX C编译器,特别是那些创建COFF和ELF目标文件的编译器,不再添加下划线.

在C编译的汇编结果中加上下划线只是一个名称错误的约定,它作为一种解决方法而出现.它(据我所知)没有特别的原因,现在已经进入了Clang.

在汇编之外,C标准库通常具有以下划线为前缀的实现定义函数,以传达神奇性的概念,而不是将其触及偶然发现它们的普通程序员.

  • 至于C源代码中的前导下划线:这是名称间距问题,参见 C标准第7.1.3节.更坦率地说:如果你的C代码定义了一个以两个下划线开头的标识符或一个下划线后跟一个大写字母的标识符,它就会被破坏.*如果它定义了带有下划线的文件范围或更大的标识符,那么它是*破坏.*这些是为编译器和标准库实现保留的. (9认同)
  • @Lars:遗憾的是,在半系统级别但不是标准库*代码中有很多臃肿的自我,比如遗留的X库,声音库,图形库等等,他们认为他们有权使用下划线,好像他们是标准库...然后有些人盲目地从标准库的各种实现中导入代码而不理解它,并保留下划线......这些用法肯定是*破坏*. (4认同)

cHa*_*Hao 5

许多编译器用于将C转换为汇编语言,然后在其上运行汇编程序以生成目标文件.它比直接生成二进制代码容易得多.(AFAIK GCC仍然这样做.但它也有自己的汇编程序.)在此转换过程中,函数名称成为汇编源代码中的标签.但是,如果你有一个叫做(例如)的函数ret,一些汇编程序可能会感到困惑并认为它是一个指令而不是一个标签.(例如,YASM主要是因为标签几乎可以出现在任何地方而且不需要冒号.$如果你想要一个标签,你必须预先添加一个标签ret.)

将字符(例如,下划线)添加到C生成的标签比编写自己的C友好的汇编器或担心标签与汇编指令/指令冲突要容易得多.

如今,汇编程序和编译器已经发生了一些变化,大多数人无论如何都在C级或更高级别工作.因此,在C中破坏名称的最初需求基本消失了.