重载 — c++ name mangling

366 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情

在c++ 中,众所周知,为了和c语言向下兼容,c++做出了很多的牺牲和让步,在这里面,函数,比c语言中的函数更要复杂和麻烦。

c语言中的函数是不具有重载的,所以函数名即名字,在编译链接时,若是两个文件中有重复的名字最多报一个错误而已,而c++中,却多出了不少问题:

普通函数重载

首先,拥有的函数重载,重载的名称问题。

拥有了一些东西,你也失去了一些东西。

int foo() {}
int foo(int x) {}

int main() {
  std::function<int(int)> f = foo;
  return 0;
}

试试看上述代码是否可以编译通过,若是不行,如何改正?

重载给我们带来了一种假象,让我们可以传不同的参数进去然后转发到不同的函数去执行。

看着很美好,其实并不是那么的美妙。

先看看c++的重载是如何做的吧!

现在我们有一个不重载的函数:

image.png

nm来看看其名字是如何的:

image.png

现在,nm告诉我们,这个函数的名字叫做 _Z4testv

多写几个函数试试:

image.png

nm看看结果

image.png

发现,名称其实还是有一些规律,

_Z4为前缀,参数名其后依次排序(g++里面int参数名是i,double是d)

可以使用c++filt来进行解析名称

image.png

那么,我们可以就直接用这个名称进行调用吗?

讲道理,在汇编层面,我觉得是可以的,来试试看

先看看汇编上的名字是不是刚才那样的名字

image.png

c++也遵循c语言的abi,所以,按理来说,c语言可以直接链接c++编译出来的二进制文件

现在我们的c语言调用代码如下:

image.png

可以发现,我们只是简单的声明了一下即可,为了链接地址所用

现在将 c文件编译,然后和c++的这个demo文件进行链接

image.png

image.png

然后,我们就可以对这个名字直接进行调用


归根结底,其实我们发现,c++重载无非就是进行名称重写罢了,没什么稀奇的。

但是,有时候,重载却会给你带来一些坑

比如下列代码:

#include <stdio.h>
void fun(int) {
  printf("fun int\n");
}

void test() {
  fun('a');
}

void fun(char) {
  printf("fun char\n");
}
int main() {
  test();
  return 0;
}

你觉得应该是输出什么呢?

反正在我的机器上输出的是 fun int

我机器是编译器是:gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)

成员函数or命名空间内函数重载

那么,上面说了普通函数的重载,成员函数重载又是什么样子呢?

image.png

nm 打印出来是这个样子的:

image.png

可以看到,这个名字为什么和普通重载的不一样呢?

那么,命名空间内的函数重载形式是什么样子呢?

image.png

image.png

_ZN4Demo3funEi很好分析,意思是 4 Demo 3 fun,前面的命名空间是Demo,然后是Demo下的fun函数

数字用于分割参数与函数名之间的区别


题外话

若是c语言要调用c++现有内容,如何做呢?

正规做法: 采用 extern "C"的表示来不让起进行 name mangling

不正经的做法:c语言用已有c++库如何做呢,此时c++并没有兼容c的想法,意思是c语言很尴尬的调用不到同名的函数,因为被重载掉了,找出 name mangling 后的函数名,手动构造所需参数进行调用(自己玩玩就好了,不能真的用在实际中)