目录
1. 函数重载的概念
2. 函数重载注意点
3. C++可以函数重载的原因
4. 总结
1. 函数重载的概念
函数重载:是函数的一种特殊情况。C语言不支持函数重载,而C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
2. 函数重载注意点
仅仅修改函数返回类型不是函数重载因为无法区分你要调用的是那个函数。
3. C++可以函数重载的原因
- 既然函数重载这么方便,为何C语言就不行呢?
- C++又是如何支持函数重载的呢?
接下来,我就展开来讨论下:首先,为了更好的显现出其具体操作过程,我将在Linux的环境下向大家展示其具体过程。其次,解释其原理需要借助我们之前讲解过的程序的编译链接,所以下文我也会带着再简要讲解下。所以,正文开始:
Linux环境下演示函数重载:
- gcc编译 -- C语言版本
首先,我们在Linux环境下创建3个目录:f.h、f.c、test.c。来分别进行声明、定义、实现。
注意后缀,都是以.c命名,这说明以下操作是在C语言的情况下进行的。
并且对上述代码编译运行后没有错误,接下来用gcc编译对它生成tc可执行程序,用g++编译对它生成tcpp可执行程序,并且两个文件编译运行均没错误。
接下来,我们在原有文件的基础上再写一个函数来确保其是函数重载 。
用gcc对它进行编译:
很明显发生错误,再强调下,上述操作是在C语言的基础上完成的。这就足矣说明,C语言是不支持函数重载的,想要搞清楚原因前,就要先明白程序的编译链接,看下文:
回顾程序的编译链接:
针对上述的三个目录文件:f.h、f.c、test.c,接下来展开讨论:
程序的编译链接分为四大过程:
- 预处理 -- 头文件展开、宏替换、条件编译、去掉注释。预处理后生成f.i和test.i文件
- 编译 -- 检查语法,生成汇编代码。编译后生成f.s和test.s文件
- 汇编 -- 把汇编代码转换成二进制的机器码。汇编后生成f.o和test.o文件
- 链接 -- 合并段表、符号表的合并和符号表的重定位。通俗讲就是找调用函数的地址,链接对应上,合并到一起
看图:
- 首先预处理:
- 编译,会生成符号表(记录的是函数定义和函数地址的映射)以及函数调用指令
这里生成了main函数的指令,其中有f,因为还不知道确切的地址,只是有声明,所以先用?表示,接着进入链接,找调用函数的地址,链接对应上,并合并到一起 。随后就在编译形成的符号表里寻找与main函数指令相同的函数名,并找到其地址。
采用C语言编译器编译后结果:
采用如下指令进行编译:
结果如下:
gcc的函数名修饰规则:
直接以函数名命名,没有任何其它的修饰,这么做也就注定造成了出现多个相同函数名的时候,在链接时call不知道链接哪个,因为函数名都是一样的,找不到其地址,这也就说明了C语言不支持函数重载,其链接过程的图示和上述图示一样:
采用C++编译器编译后结果:
采用如下指令编译:
结果如下:
- 函数一:
- 函数二:
C++的函数名修饰规则:
仔细观察C++版本的汇编指令,观察两个不同函数的函数名修饰样式:
- 一个是<_Z1fid>:
- 另一个是<_Z1fdi>:
有没有发现它把参数类型的首字母带进去了,那也就意味着你的参数的类型不同,个数不同,参数顺序不同都会导致函数名不同
这个时候,C++编译后生成的符号表里以及链接时函数调用指令应该是这个样子:
这个时候,C++在链接的过程中,call找的就是其修饰后的函数名,函数名不同,自然不会出错,这就是C++支持函数重载的核心所在,而C语言的函数命名规则是根据函数名设定的,函数名相同的话,链接就会出错,找不到确切地址,自然不会支持函数重载
再来确定下C++函数名的修饰规则:
_Z 函数名长度 函数名 类型首字母而返回值的不同并不会影响到函数名的修饰规则,这也就是为什么前面强调的函数返回类型不同不支持函数重载。
4. 总结
C++支持函数重载而C语言不支持是因为函数在内存中的存储方式不相同,C语言是直接以函数名修饰,而C++是_Z 函数名长度 函数名 类型首字母,导致C++支持重载,而C语言不支持重载。