函数重载你真的了解吗?

news/2025/3/15 2:52:16/

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 来进行的。以下是一些基本规则:

  1. 函数名修饰格式: C++ 编译器根据函数的参数类型、参数个数、以及一些其他信息生成一个唯一的函数名。这个过程称为 name mangling。修饰后的函数名通常包含原函数名以及一些用于标识参数类型和个数的信息。

  2. 名称修饰的参数类型表示: 常见的参数类型表示包括基本数据类型、指针、引用等。例如,int 类型可能用 i 表示,double 类型可能用 d 表示。

  3. 参数个数和其他信息: 修饰还包括参数的个数和其他一些信息,以区分函数重载和模板特化等情况。

观察以下函数

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++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载

同时需要注意的是,函数名修饰的具体的修饰规则可能会因编译器版本、编译选项、操作系统等因素而有所不同。在实际的应用中,除非涉及到与其他语言或库的交互,否则大多数情况下不需要深入了解或处理函数名修饰。


http://www.ppmy.cn/news/1340762.html

相关文章

怎么控制Element的数据树形表格展开所有行;递归操作,打造万能数据表格折叠。

HTML <el-button type"success" size"small" click"expandStatusFun"> <span v-show"expandStatusfalse"><i class"el-icon-folder-opened"></i>展开全部</span><span v-show"expan…

qt编程---->qml

qml简介&#xff1a; QML是一种用于构建用户界面的声明性编程语言&#xff0c;由Qt开发框架提供支持。它允许开发人员使用简单的语法和组件来创建各种类型的用户界面&#xff0c;包括移动应用、桌面应用和嵌入式系统。同时也支持qml和C的混合编程 QML采用了类似JavaScript的语法…

Java List中对象根据id去重,并处理重复对象的某个字段

List中对象根据id去重 一、需求二、解决 一、需求 参考文章:https://blog.csdn.net/A_Gui_Code/article/details/106978867 对在list集合中对象根据主键id去重&#xff0c;同时需要对重复对象的某个字段进行单独处理。 例如&#xff0c;对象包含字段如下, 当某个对象重复时&a…

linux免密登录的实现

ssh免密登录使用方便&#xff0c;关键没有了口令验证反倒规避了暴力破解或者被探测的风险。配置得当&#xff0c;使用ssh免密登录更加安全。在生产环境中应用和数据库服务器之间互相设置后使用方便&#xff0c;并且在第三方人员配置使用时不用告知对方密码。 第一步、ssh登录发…

【毕业日记】2024.01 - 慢下来,静待花开

转眼距离930离开鹅厂已经120天了&#xff0c;我是很能拖延的&#xff0c;或者是很懂自我麻痹的&#xff0c;这三个多月&#xff0c;一直想要写点东西纪念&#xff0c;一直拖一直拖一直拖…… 疫情这几年经济下行里裁员是个茶余饭后“嬉笑”之余经常被提起的词&#xff0c;部门滚…

Java对象属性设置方式

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 对象属性设置方式 直接赋值 SystemMappingCode systemMappingCode new SystemMappingCode();systemMappingCode.setCodeResp(request.getCodeResp());systemMapping…

leetcode刷题(剑指offer) 103.二叉树的锯齿形层序遍历

103.二叉树的锯齿形层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 示例 1&#xff1a; 输入&#xff1a…

【JavaEE进阶】 图书管理系统开发日记——叁

&#x1f334;前言 在前面我们实现了用户登录的接口。现在我们来实现图书列表展示页面。 &#x1f38b;数据准备 创建图书表&#xff0c;并初始化数据 -- 图书表 DROP TABLE IF EXISTS book_info; CREATE TABLE book_info (id INT ( 11 ) NOT NULL AUTO_INCREMENT,book_nam…