目录
本篇可以解决的问题:
为什么C语言没有函数重载,为什么C++有函数重载?
C++如何实现函数重载?
问题引入
首先回顾一下什么是函数重载?
函数重载相当于一词多义,就是说有多个同名函数,但是参数列表不同(数量、顺序、类型),会根据参数的不同而进入不同的函数,用来解决不同但类似的问题。
请问下面这种情况是不是函数重载?
short Add(short left, short right) {
return left + right;
}
int Add(short left, short right) {
return left + right;
}
答:函数重载与返回类型没有关系,所以明显不是函数重载,在一些编译器中会报错。
而我们今天就深入探讨一下,为什么函数重载和返回类型没有关系?又为什么和参数类型、顺序、个数有关系?
1. 函数重载(C++)
因为是偏向底层的讲解,所以这里不适用VS(IDE),而是使用Linux来展现整个过程。
首先我们回顾一下学习C语言的过程中,可执行文件的生成过程?
1.1 编译链接过程
预处理 -- 头文件展开,宏替换,条件编译,去掉注释
编译 -- 检查语法,生成汇编代码
汇编 -- 把汇编代码转换成二进制的机器码
链接 -- 找调用函数的地址,链接对应上,合并到一起 合并符号表
符号表补充知识:
符号表里面有什么?
目标文件中引用的全局变量和函数
目标文件中定义的全局变量和函数
本质上符号表表达的内容?
我能提供给其他文件使用的符号
我需要其他文件提供给我的符号
1.1.1 预处理
这里我们先创建三个文件,分别是 f.h、f.cpp、test.cpp:
预处理之后,头文件被展开:
1.1.2 编译
举一个例子:
int main() { Add(3, 0.14); Add(0.141, 3); return 0; }
反汇编代码如下:
1.1.3 汇编
因为机器只能识别0、1,并不认识指令级代码,所以这一步需要将指令级代码转换成二进制的机器码。
1.1.4 链接
找调用函数的地址,链接对应上合并到一起,合并符号表。
在编译的过程中还会生成一个符号表,主要记录函数定义和函数地址的映射,在main函数的指令中,有两句指令call,call后面所跟的就是函数的名字和函数的地址。
为了更好的看清楚函数的命名规则,我们在Linux中来看一看:当函数名相同,参数不同时,他们的函数名会有一套新的命名规则。在不同的函数调用中,他们参数类型首字符带进命名规则中去了。
新的函数名:_Z 函数名长度 函数名 类型首字母
也就是图中的:
- _Z1fid
- _Z1fdi
1是函数名长度,f是函数名,id(类型首字母PS:整形指针的是Pi)是int和double。也注意!这里没有解释返回类型的指令,这也可以说明为什么C++的返回类型不能作为函数重载条件的原因!
2. C语言中的处理
创建三个文件:
我们再在Linux下看看C语言是如何处理函数名的
可以看出,C语言对于函数的描述只有函数名本身,并没有参数的相关信息。
我们在Linux下看看报错信息:我们创建两个f函数,我们在Linux下用gcc编译看报错信息,我们发现这时就会报错说函数命名发生冲突。
3. 总结
- C++支持重载的原因是,再生成符号表示,对于函数的命名规则有了新的变化:_Z+函数名长度+函数名+类型首字母
- 而在新的命名规则下,依然没有返回类型的相关信息,因此返回类型对于函数重载没有影响
- C语言对于函数命名的处理中,只有函数名而不涉及其他参数,所以C语言并不支持函数重载