C++支持重载的幕后---名字修饰规则(调用约定、extern "C")

C语言名字修饰

我们知道C语言是不支持函数重载的,而C++支持。原因很简单,编译器对C函数的修饰和对C++函数的修饰是不同的。

编译器对于C语言的修饰是在其函数名前加一个下划线,只与函数名相关,与其他无关。

C++名字修饰规则的由来

我们都知道,C++是一门强大又复杂的语言,包含类、继承、虚函数、重载、命名空间等,使得符号管理更加复杂。
举个简单的例子:

void fun(int a);
void fun(double a);

这两个函数只是参数列表不同,是C++中函数重载的最简单的一种情况,那么编译器是如何区分这两个函数的?
为了支持C++这些复杂的特性,人们发明了名字修饰的机制,下面我们来看看C++的名字修饰机制。

名字修饰

C语言在链接时是通过函数名找要调用的函数,给函数名前加下划线。C++支持重载是在链接时,根据函数名和参数类型找要调用的函数。

名字修饰规则比较复杂,C++对于名字修饰的规则(作用域,函数名,参数列表)我们来测试一下,vs和linux底层如何实现的。

#include<iostream>
using namespace std;
int add(int x,int y)
{
      return x+y;
}
float add(float x,float y)
{
      return x+y;
}
int main()
{
     int a = 1;
     int b = 2;
     float c = 1.2;
     float d = 2.1;
     cout<<add(a,b)<<endl;//调用add(int,int)
     cout<<add(c,d)<<endl;//调用add(float,float)
     return 0;                                                                  
}

Linux下:

objdump -d a.out > add.txt

这里写图片描述
-Z3是与返回值有关的,后边跟的是函数名,最后是参数类型。

vs 2013下:

这里写图片描述
该编译器下是按照__cdecl调用约定的。

调用约定

__cdecl调用约定

  • 以”?”表示函数名的开始,后跟是函数名;
  • 函数名后面以”@@YA”标识参数表的开始,后跟参数表;
  • 常用参数表的代号
参数表 解释 参数表 解释
X void J long
D char K unsigned long
E unsigned char N double
F float M float
H int _N bool
I usigned char PA 指针

__stdcall调用约定

__stdcall 与上面的 __cdel调用约定一样,只是参数表开始的标识上面的“@@YG”变为“@@YA”。

__fastcall调用约定

__fastcall与上面的 __cdel调用约定一样,只是参数表的开始标志由上面的”@@YG”变为“@@YI”。

extern “C”

extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用.
extern “C”是指在C++中调用C中的关键字,相当于告诉编译器这部分的代码是使用C语言的规则进行编译和链接的。

猜你喜欢

转载自blog.csdn.net/zwe7616175/article/details/81089168