编译器使用一种称为名称修饰的技术。
简而言之,编译器将参数的数量和类型编码为写入目标文件的实际名称。Wikipedia 文章中有一些关于该主题的示例,包括来自 C++ 的示例。
作为一个具体的例子,我在 Mac 上使用 g++ 编译了以下 C++ 文件:
测试.cpp
int f(int x) {}
int f(double x, char y) {}
和
g++ -S test.cpp
这会生成汇编语言文件(有些省略):
测试.s
.globl __Z1fi
__Z1fi:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
leave
ret
.globl __Z1fdc
__Z1fdc:
pushq %rbp
movq %rsp, %rbp
movsd %xmm0, -8(%rbp)
movb %dil, -12(%rbp)
leave
ret
这里重要的部分是函数被调用__Z1fi
并__Z1fdc
在汇编语言输出中,链接器将看到。您可能可以推断出这f
是函数的名称,对于参数,我们有i
(int) 和dc
(double 和 char)。请注意,参数的顺序也是编码的!
现在考虑如果你有会发生什么
int f(int x) {}
int f(int y) {}
就语言而言,这当然不是可接受的情况,因为f(10)
无法解决类似的呼叫。理论上,一种语言可以指定第二个声明替换第一个声明,但 C++ 不会这样做。这简直是非法的重载。
事实证明,名称修饰实际上说明了为什么这应该是一个错误。编译器会尝试使用名称创建两个不同的函数__Z1fi
(实际名称不是由语言定义的,而是依赖于编译器的)。在这个级别的程序中,我们不能有两个同名的函数。