在解释原因之前,我们首先需要了解一些基本知识
1、什么是重载??
重载:函数名相同时,参数类型、参数个数、参数顺序,三者任意一者不同即可构成重载
2、程序运行时干了啥?——以func.h为例
程序运行起来需要经过下面几个阶段:
(1)预处理 —— 头文件展开、宏替换(#define)、条件编译(#ifdef)、去除注释(生成 .i 文件)
(2)编译 —— 检查语法,若无误,生成汇编代码(生成 .s文件)
(3)汇编 —— 汇编代码转换成二进制机器码(生成 .o文件)
(4)链接 —— 生成可执行文件
在预处理阶段,展开头文件func.h,将函数声明裸露在main函数中(后面通过实例说明)
在编译阶段,检查语法,C和C++根据各自的编译命名规则,生成 对应的函数名
在汇编阶段,生成 func.o文件以及func.o的符号表(编译阶段生成的函数名:函数地址)
在链接阶段,根据编译阶段生成的函数名 去符号表找 函数地址,以此找到函数定义
3、C 和 C++ 在编译时 生成函数名的编译规则??
C编译规则: 直接使用函数名
func(int x) =======》 编译后生成的函数名:func
符号表 =======》 func:4500232
C++编译规则: _Z + 函数名长度 + 函数名 + 形参类型
func(int x) =======》 编译后生成的函数名:_Z4funci
符号表 =======》 _Z4funci:4500232
编译生成汇编代码的过程中,C和C++对函数的处理就体现出来了
下面将在Linux系统上分析,因为Linux上的函数名修饰规则比较 清晰
目录
一、准备工作
在Linux中新建func.h 、func.c、test.c文件
编译生成 .out文件(等价于windows下的exe文件)
二、原理解析
预处理 =======》展开 func.h,func.h中的函数声明和main函数在同一个文件中
汇编 =======》 检查是否有语法错误,如果是C 编译,则按照C的规则生成 汇编函数名
如果是C++编译,则按照C++的规则生成 汇编函数名
编译 =======》 生成二进制机器码,即.o文件(计算机只能识别二进制机器码)
生成func.o的时候,会顺带一个符号表
链接 =======》 C程序:call func(?),根据 func 去符号表找函数地址,执行函数体
C++:call _Z4funci(?),根据 _Z4funci 去符号表找函数地址,执行函数体
PS:
(1)call 是汇编语言中调用函数的指令,存在于.s文件中,这里意为调用func函数
(2) func(?) —— 调用函数的时候只知道函数名,但不知道函数地址
在符号表中找到对应的函数地址以后, ?会被替换为函数地址,如 func(40052d)
三、C 测试
(1)单个函数
在Linux命令行输入 objdump -S ./a.out
调用函数时,执行指令 call <func>
===>C程序调用函数时,也就是链接阶段,直接通过 函数名 去找 函数地址,不考虑参数个数或者参数类型
(2)多个同名函数
假设我们又定义了一个 func(int a) ,
函数调用的时候,都是 call func(?),在符号表中有两个 func ,C 程序不知道要调用哪个函数
所以就会报错
四、C++测试
(1)新增重载函数
我们在原本的基础上新增一个函数
(2)程序测试
我们会发现,汇编使用的函数名,不是一样的!!
C++有自己的名字修饰规则:_Z + 函数名长度 + 函数名 + 形参类型
call 指令会根据 修饰后的函数名 去符号表找 函数地址,然后通过函数地址链接到对应的函数定义
找到 函数地址 后,把 ?换掉
五、总结
C语言:直接根据 函数名 去符号表找函数地址
C++:有自己的修饰规则,能够根据 修饰后的函数名 映射到地址