C++:详解函数重载(详细探究Windows和Linux下函数的修饰规则)


1. 函数重载

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题,重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

概念什么的太复杂,总结下来就两点

  • 函数名字相同
  • 函数的参数列表不同。(即参数个数类型顺序必须不同)

或换句话来说就是在相同的地方,却有不同的含义,就比如 a * b 和 * p一样,符号" * "就是被重载了。
举个例子:

  • int Add (int left, int right)
  • double Add (double left, double right)
  • long Add (long left, long right)

以上这三个函数都属于函数重载。那么,double Add (int left, int right) 与上面函数重载吗?
答案是毋庸置疑的,不是函数重载,带入情景中去想,若一个main函数中调用了 Add(1,2) 函数,那么它该调用哪个函数,由于参数相同,调用 int Add (int left, int right)double Add (int left, int right) 都是可以的,因此就会发生错误。由此,我们可以得到一个结论:函数重载和返回值的类型无关

在初学C++的时候,相信老师都说过,C++支持函数重载,而C语言不支持函数重载,但是为什么呢?
为什么C++支持函数重载,而C语言不支持函数重载呢?

2. 函数重载的深度探究

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接
一个项目通常都是由多个头文件和多个源文件构成的,而当在a.cpp中调用b.cpp的Add函数时,在经过预处理、编译之后,计算机只是检测了b.cpp中是否有Add函数的声明,但是a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中,那该怎么调用呢?
因此,在链接阶段,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。
而在链接的时候,面对Add函数,每个编译器都有着自己的函数名修饰规则,在经过编译后,不同的编译器中对函数名的修饰是不同的,下面将分为在Windows操作系统和Linux操作系统下对函数命名修饰规则进行探索。

如果对这些不是很了解的话,可以去看我之前写的C语言-详解程序的编译+链接

2.1 Windows下名字修饰规则

由于在VS中编译器对函数名的修饰的规则不够明显,不是很容易看出函数名的差别(VS中是直接显示的函数的地址,没有直接的显示出修饰后的函数名),因此我们在VC++中对其进行探索。

首先给出要进行测试的C++代码,如下:

#include<iostream>
using namespace std;

int Add(int a, int b)
{
	cout << "test to overLoad (int Add)" << endl;
	return a + b;
}

void Add(double a, double b)
{
	cout << "test to overLoad (void Add)" << endl;
}


int main()
{
	cout << "test start!" << endl;
	int k = Add(1, 2);
	Add(1.1, 2.2);
	return 0;
}

进入调试,当走到 int k = Add(1, 2) 时,打开反汇编代码进行查看,查看当前Add函数的命名。

C++编译器下
在这里插入图片描述
由于VC++中显示的不是很清楚,我将该信息复制到txt文件中,进行详细的分析
在这里插入图片描述

对比一下发现修饰后的格式为:? + 函数名 + @@YA + 返回值 + 参数1 + 参数2 + @Z,int类型对应的是字母H,void类型对应的是字母X,double类型对应的是字母N。扩展:float类型对应的是字母M

我们可以看到函数名、参数的类型都被加入了修饰后的名称中,这样编译器和链接器就可以区分同名但不同类型参数的函数,就能及时的找到对应的函数。

还是同样的代码,只不过这次我们修改为C程序后对其进行调试,对其进行查看,但是我们很快就能发现,程序连编译都无法编译,但是为了查看C编译器的函数修饰规则,我们只对一个Add函数进行测试

C编译器下:
在这里插入图片描述

可以发现C编译器下修饰的格式为:_ + 函数名,因此,若遇到函数名相同的情况(即函数重载),C编译器就无法判断到底调用哪个函数,就会发生错误,这才是为什么C++可以重载函数,而C不能重载函数的真正原因。即C语言因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

2.2 Linux下名字修饰规则

同样的道理,还是用相同的代码,我们在Linux下首先使用 g++ test.cpp -o main,生成一个可执行程序main,然后再执行objdump -S main语句,即可查看该程序的反汇编代码,由于使用该语句,反汇编代码会直接输出到命令行窗口上,因此我们可以将结果重定向到一个文件当中,使用objdump -S main > log.txt,然后对log.txt文件进行分析

C++编译器下
在这里插入图片描述
分析log.txt文件
在这里插入图片描述

对比一下发现修饰后的格式为:_Z + 函数名长度 + 函数名 + 参数1类型首字母 + 参数2类型首字母扩展:若参数是指针的话,比如 int* p,那它会被修饰为 pi ,这个其实也证明了重载函数与其返回值无关。Linux和Windows的编译器下,虽然函数修饰的格式不一样,但是大概的风格都是一样的,均是函数名+参数列表的形式

C编译器下:
在这里插入图片描述

与Windows一样,Linux下C编译器下的修饰格式是:函数名,是的,就一个函数名,同上,是肯定不会支持函数重载的。

碎片知识:有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值