为什么C++支持函数重载而C语言不支持?
为什么C++支持函数重载而C语言不支持?
主要代码
先看工程中的三段代码
//文件名 f.h
#include <stdio.h>
int Add(int a,int b);
double Add(double a, double b);
//文件名 f.c
#include "f.h"
int Add(int a,int b){
return a + b;
}
double Add(double a, double b){
return a + b;
}
//文件名 test.c
#include "f.h"
int main(){
int a = Add(1,2);
double b = Add(1.1,2.2);
printf("%d,%f\n",a,b);
return 0;
}
运行结果
C语言编译:
C++编译结果:
上述代码是对函数重载的简单运用,但在C语言编译下和C++语言编译下结果不同,因为C++语言支持函数重载,而C语言不支持,但为什么C语言不支持函数重载呢?
程序编译过程
在C/C++中一个程序运行起来需要经历几个过程:
阶段文件:f.h f.c test.c
1、预处理 -> 头文件展开、宏替换、条件编译、去掉注释
f.i test.i
2、编译 -> 检查语法,生成汇编代码
f.s test.s
3、汇编 -> 把汇编代码转换成二进制代码
f.o test.o
4、链接 -> 把几个二进制代码文件整合成一个可执行文件
a.out(linux系统中)
在链接的过程中并不是代码的简单合并而是需要关注被调用函数的地址,先来看C环境下main函数的汇编代码(为了程序能成功编译,将int Add 改为int Addi:
在40057f和4005ad处是对int Addi(int a,int b),double Add(double a,double b)的调用
call + 函数地址是汇编语言对于函数调用的语法,但上述汇编代码是在程序连接完成后生成的,在第二节程序编译过程中的汇编阶段就需要生成汇编代码。在这个过程中test.s 中关于函数调用的汇编代码临时写为 call ? Addi ,中间的?是Addi函数的地址,在这个过程中还不能确定,只有在链接的过程中才能确定,在链接的时候是如何确定函数地址的呢?
这是因为在编译过程中会为每个文件生成一个符号表,用于存放函数地址,例如f.c文件在C语言中的符号表可以表示为:
函数名 | 函数地址 |
---|---|
Add | 400541 |
Addi | 40052d |
在链接的过程中C语言编译器就会根据函数名去寻找函数地址,但是如果有重名函数,编译器就会无法根据函数名去寻找函数地址,因为符号表里有两个Add,无法确定哪个才是需要的函数地址。
那么C++是如何实现函数重载的呢?
我们来对比一下C 和C++编译出的汇编代码
C++汇编代码:
我们会发现在函数调用语句中C++使用的函数名是 _Z3Addii 和_Z3Adddd
4005af: e8 a9 ff ff ff callq 40055d <_Z3Addii>
4005dd: e8 8f ff ff ff callq 400571 <_Z3Adddd>
原来 C++使用了独特的函数名修饰规则,使得同名函数的重载,在符号表里会有不同的函数名,
修饰规则(以_Z3Addii为例):
_是固定前缀,Z为linux系统固有前缀,3为函数名符号长度,Add为函数名,长度为3,最后的ii为函数参数的类型。
C++编译下f.c文件的符号表:
函数名 | 函数地址 |
---|---|
_Z3Addii | 40055d |
_Z3Adddd | 400571 |
这样函数在编译的过程中对于同名函数的不同重载就会有不同的函数修饰名,就不会链接出错。