1.什么叫函数重载?
函数重载(Function Overloading)是指在同一个作用域内,允许定义多个具有相同名称但参数列表不同的函数。具体而言,函数重载允许你定义同名的函数,但这些函数应该有不同的参数类型、参数个数或者参数顺序。
举例
上述代码中有两个名为Print的函数,虽然名字一样,但是参数类型不一样。在c语言中会认为这两个是一个函数,后一个定义的会覆盖前面一个。在c++中函数重载的概念引入之后便认为这两个函数是不同的两个函数,并会根据传参类型自动选择相应的函数,由此一来我们的代码会变得更加灵活和易读。
2.参数不同构成函数重载
值得注意的是,如果仅仅是函数返回值不同是无法构成函数重载的。在函数调用时,编译器会根据实际传递的参数类型来确定调用哪个函数,而不是仅仅依赖于返回值类型。
3.函数重载的原理
为什么c语言不支持函数重载而c++支持呢?
在c/c++中,一个程序要想运行起来需要经历以下几个阶段:预处理、编译、汇编、链接。
预处理会将.c/.cpp文件中的代码经过头文件展开、去除注释、宏替换、条件编译转换成 .i文件
编译会将 .i 文件中的代码翻译成汇编代码,生成 .s文件
汇编会将 .s中的汇编代码翻译成二进制指令,生成 .o文件(也叫目标文件)
链接会将程序所有的 .o 文件进行链接生成 .exe后缀的可执行文件
各个阶段具体是怎么实现的可以去看
c语言的程序环境和预处理(一眼丁真)_c语言环境打击那-CSDN博客
其中,在链接这个阶段会进行合并段表、符号表的合并和符号表的重定位等行为。简单来说,链接操作通过合并这些符号表等实现了程序中各个文件的连接,也就是能在一个文件中通过符号表找到另一个文件定义的函数地址并执行这个函数。
1.实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们
可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标
文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么
怎么办呢?
2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就
会到b.o的符号表中找Add的地址,然后链接到一起。(老师要带同学们回顾一下)
3. 那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
4.函数名修饰规则
函数名修饰(Name Mangling)通常是指编译器对函数名进行的一些变换或修改,以便区分同名函数或支持函数的重载。在C语言中,函数名修饰是相对较简单的,因为C语言本身不支持函数的重载。
4.1对比c/c++处理函数名
C语言中的函数名不会被修饰: 在C语言中,函数名不会被编译器修改或添加任何前缀或后缀,因为C语言本身不支持函数的重载。因此,C语言的函数名在源代码中的写法和编译后的二进制代码中是一样的。
C++语言中的函数名修饰: C++支持函数的重载,因此编译器需要在编译阶段对函数名进行修饰,生成唯一的函数名以区分不同的函数。修饰的方式通常包括添加函数参数类型、参数个数等信息。
4.2c++具体是怎么修饰函数名的呢?
以linux举例,在Linux中,
g++
编译器使用 C++ ABI (Application Binary Interface) 标准,其函数名修饰规则是按照 Itanium C++ ABI 来进行的。以下是一些基本规则:
函数名修饰格式: C++ 编译器根据函数的参数类型、参数个数、以及一些其他信息生成一个唯一的函数名。这个过程称为 name mangling。修饰后的函数名通常包含原函数名以及一些用于标识参数类型和个数的信息。
名称修饰的参数类型表示: 常见的参数类型表示包括基本数据类型、指针、引用等。例如,
int
类型可能用i
表示,double
类型可能用d
表示。参数个数和其他信息: 修饰还包括参数的个数和其他一些信息,以区分函数重载和模板特化等情况。
观察以下函数
void myFunction(int x, double y);//修饰后的函数名为:_Z10myFunctionid
g++编译器对该函数名修饰后变成了_Z10myFunctionid,在这个例子中,
_Z10
是由编译器添加的前缀(不同编译器可能不一样),myFunction
是原函数名,后面的id
表示参数的类型,i
表示int
类型,d
表示double
类型。
4.3用汇编代码查看函数名是否被修饰
观察以下代码
以上代码是在linux中的vim编译器运行的,具体的操作指令大家无需了解。我们只需要知道gcc是编译c语言的编译器,而g++是编译c++代码的编译器。我们现在用两种方式去编译同一份代码,并观察其 汇编阶段生成的 .s 文件,这样我们就能在汇编层面观察到函数名被修饰的样子。
用gcc编译
用g++编译
我们可以看到,add函数名被修饰成了_Z3addii 其中后缀"ii" 指的是两个参数的类型。这样一来,两个函数的名字名即使相同,我们也能因为其参数不同生成两个不同的“函数名”。
5.总结
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。同时需要注意的是,函数名修饰的具体的修饰规则可能会因编译器版本、编译选项、操作系统等因素而有所不同。在实际的应用中,除非涉及到与其他语言或库的交互,否则大多数情况下不需要深入了解或处理函数名修饰。