我的C++奇迹之旅相遇:支持函数重载的原理

请添加图片描述

文章目录

  • 📝前言
  • 🌠 C++支持函数重载的原理:名字修饰(name Mangling)
    • 🌉不同编译器不同函数名修饰规则
  • 🌠Windows下名字修饰规则
  • 🚩总结


📝前言

函数重载概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

//参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}

double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}

//参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}

//参数类型顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

🌠 C++支持函数重载的原理:名字修饰(name Mangling)

为什么C++支持函数重载,而C语言不支持函数重载呢?

C++通过名字查找、名字修饰、解析和链接这几个步骤,实现了函数重载的功能。名字修饰产生唯一内部名称,是支持重载的关键。但在程序运行时,仍然使用原来的外部函数名称调用,这是函数重载的一个重要特点。

什么是名字修饰:
名字修饰(Name Mangling)是C++编译器为函数、类等名称添加额外信息的过程,目的是为了区分重载重定义等名称。

名字修饰的原理
名称修饰是编译器在编译源代码时为函数、类等名称添加额外信息的过程,生成内部链接名称。该内部链接名称包含原名称以及其他信息,如参数类型、返回类型等。
这样就可以区分函数重载、重定义等情况,生成唯一的内部名称。链接器根据这些内部名称进行链接。但程序在调用时仍然使用原外部未修饰的名称

例如一下C++的函数重载:

int func(int a);
int func(double b); 

编译器可能为它们生成以下内部名称:

_Z4funci // func(int)
_Z4funcd // func(double)

这里_Z4func是原名称,后面的id表示参数类型。因此,即使两个函数的原名相同,但在编译器进行编译处理后,根据参数的类型进行标记,获得了不同的名字标识符。所以,当编译器根据内部名称的不同,就可以将他们区分开来。

当然,更细化的理解,应该是这样的:在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接

实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道编译和链接他们各自都干了不少事,首先,我们先吧一个项目分为3个文件:Stack.h,Stack.cpp ,Test.cpp

  1. Stack.h
#pragma once
#include<iostream>
using namespace std;

struct Stack
{
};

void StackInit(struct Stack* ps, int n);
  1. Stack.cpp
#include"Stack.h"

void StackInit(struct Stack* ps, int n)
{
	cout << "void StackInit(struct Stack* ps, int n)" << endl;
}
  1. Test.cpp
#include"Stack.h"

int main()
{
	struct Stack st;
	StackInit(&st, 10);
	return 0;
}

代码运行:
在这里插入图片描述
此时程序正常运行,当我把Stack.cpp里的定义去掉后,如图:
在这里插入图片描述
再次编译运行时,代码就会报错,这个错误不是编译错误,而是链接错误,编译错误通常是语法错误。
在这里插入图片描述
再看此图,我们来分析这个为什么是链接错误,可知道当Test.cpp,Stack.cpp,Stack.h这三个文件运行起来是,先进行预处理,预处理****就是把相应的头文件展开,然后宏替换,然后条件编译等等,紧接着Stack.cppStack.h会生成Stack.i文件,Stack.hTest.cpp会生成Test.i文件,也就是.c文件分别与.h文件进行生成.i文件,生成了两个.i文件然后进行编译,编译就检查语法,生成汇编代码,两个.i文件就会生成两个.s文件Stack.sTest.s,接下来就是汇编将汇编代码转成二进制机器码,此时两.s文件将会生成对应的.o文件Stack.oTest.o,这时编译已然结束,那接下来就是将这两个文件链接起来,生成可执行程序xxx.exe

请添加图片描述

了解了以上编译的大致过程,接下来,我们把Stack.cpp里的定义还原,我们拿完整的代码来解析。
我们看以下反汇编代码图,首先进去main()主函数时,
在这里插入图片描述

可以看到函数有一堆要执行的指令函数地址,第一句指令的地址
在这里插入图片描述
当我们继续按F11进入Call这个指令时,他根据函数StackInit (0A113C5h)选择括号里的地址0A113C5h的跳转到00A113C5(注:这个地址跟0A113C5h是一样的,只是进制的表示不同),当再次运行时会继续根据函数括号的地址记性跳转
在这里插入图片描述
在这里插入图片描述
从这里看出:有函数的定义,才能生成函数一堆汇编指令,第一句指定的地址,才是函数的地址,先Stack.cpp->Stack.o最后Test.oStack.o链接到一起,合并到一起,才有地址,
在这里插入图片描述
结论:Test.cpp只有函数声明,把Stack.cpp的定义去掉,可以过,因为语法检查是匹配的,Test.cpp->Test.o过程中没有函数的地址,链接时,就要用StacklInit这个名字去Stack.o找他的地址

链接时:
1、直接用函数名字去查找,是否支持重载,不支持C语言
2、直接用修饰后的函数名字去查找,就可以支持重载C++

C++如此例子运行
在这里插入图片描述
在这里插入图片描述

这就回到了我们最初的这个概念:这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题
注意:以上情况是分多个文件才会发生这样的情况,如果你不分这么一个文件,全部放在一个文件中,就不会有这个情况,但是实际项目通常是由多个头文件和多个源文件构成。

这是大致流程图:
在这里插入图片描述

🌉不同编译器不同函数名修饰规则

那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。

int Add(int a, int b) 
{
	return a + b;
}
void func(int a, double b, int* p) 
{ 

}
int main() 
{
	Add(1,2);
	func(1,2,0);
	return 0;
}

由于Windowsvs的修饰规则过于复杂,而Linuxg++的修饰规则简单易懂,下面我们使用了g++演示了这个修饰后的名字。

  • 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。
  • 采用C语言编译器编译后结果
    在这里插入图片描述
    结论:linux下,采用gcc编译完成后,函数名字的修饰没有发生改变
  • 采用C++编译器编译后结果
    在这里插入图片描述
    结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

🌠Windows下名字修饰规则

函数签名修饰后名称
int func(int)?func@@YAHH@Z
float func(float)?func@@YAMM@z
int C :: func(int)?func@c@@AAEHH@z
int C::C2::func(int)?func@C2@c@@AAEHH@z
int N::func(int)?func@N@@YAHH@z
int N::C::func(int)?func@c@N@@AAEHH@z

我们以int N::C:func(int)这个函数签名来猜测Visual C++的名称修饰规则(当然,你只须大概了解这个修饰规则就可以了)。修饰后名字由“?”开头,接着是函数名由“@”符号结尾的函数名;后面跟着由“@”结尾的类名“C”和名称空间“N",再一个“@”表示函数的名称空间结束:第一个“A”表示函数调用类型为“..cdecl”,接着是函数的参数类型及返回值,由“@”结束,最后由“Z”结尾。可以看到函数名、参数的类型和名称空间都被加入了修饰后名称,这样编译器和链接器就可以区别同名但不同参数类型或名字空间的函数,而不会导致link 的时候函数多重定义。

对比Linux会发现,windowsvs编译器对函数名字修饰规则相对复杂难懂,但道理都是类似的,我们就不做细致的研究了。
扩展学习:C/C++函数调用约定和名字修饰规则–有兴趣好奇的同学可以看看,里面
有对vs下函数名修饰规则讲解】


🚩总结

1. 通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
2. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/504635.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

加密/ 解密 PDF:使用Python为PDF文档设置、移除密码

在数字化时代&#xff0c;文档的安全性变得越来越重要。特别是对于包含敏感信息的PDF文件&#xff0c;确保其不被未经授权的人员访问或修改是至关重要的。本文将介绍如何使用Python在PDF文档中设置密码&#xff0c;以及如何移除已经设置的密码。 目录 PDF加密基础知识 Pytho…

大话设计模式之建造者模式

建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;旨在通过将复杂对象的构建过程与其表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 在建造者模式中&#xff0c;通常包含以下几个角色&#xff1a; Director&#xff08;指挥…

新增收货地址

目录 &#x1f9c2;1.创建controller层 &#x1f953;2.创建service层 &#x1f32d;3.注意细节 &#x1f37f;4.避免dao数据暴漏 1.创建controller层 controller不做逻辑操作&#xff0c;只接受前端的数据 1.添加Api设置swagger模块名称2.RestController以json形式返回…

java计算机网络(一)-- url,tcp,udp,socket

网络编程&#xff1a; 计算机网络 计算机网络指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统、网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统。 网络协议…

C#.net8创建webapi,使用SqlSugar,仓储模式,DTO,服务层,控制层的综合应用(企业级)

本文源码地址: https://download.csdn.net/download/u012563853/89036104 源码中,也有详细的注释说明。 代码总览: 这是一个综合性比较强的文章,需要有一定的基础,没有基础的人,看了后,会全面的了解一下,有基础的人,看了后会加强认识,更加的巩固,直接在项目中去应…

vue3全局控制Element plus所有组件的文字大小

项目框架vue-右上角有控制全文的文字大小 实现&#xff1a; 只能控制element组件的文字及输入框等大小变化&#xff0c;如果是自行添加div,text, span之类的控制不了。 配置流程 APP.vue 使用element的provide&#xff0c;包含app <el-config-provider :locale"loca…

mybatis的基于注解和xml的两种实现方式详解

文章简介 本文介绍了springbootmybatis实现数据库操作的两种实现方式&#xff0c;经测试均可运行使用。整体而言注解更加简便&#xff0c;但面对一些复杂查询&#xff0c;可能需要使用到注解方式。 注解方式 pom依赖&#xff1a; <dependencies><!--连接数据库--&…

Wasm初上手

总之也是为了扩宽技术面吧。。。我也不知道为什么就想试试了&#xff0c;就酱。 参考阅读&#xff1a;极客时间《WebAssembly入门课》 安装wasm的编译器Emscripten。Emscripten 是一个“源到源”语言编译器工具集&#xff0c;这个工具集可以将 C/C 代码编译成对应 JavaScript 代…

冒泡排序---算法

1、算法概念 冒泡排序&#xff1a;它重复地走访要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c;也就是说该数列已经排序完成。这个算法越小的元素会经由交换慢慢“浮”到…

Linux企业级别日志的查找

企业级别日志的查找 查看mysql数据库的日志错误日志&#xff08;Error Log&#xff09;查询日志&#xff08;General Query Log&#xff09;慢查询日志&#xff08;Slow Query Log&#xff09;事务日志&#xff08;Transaction Log&#xff09;二进制日志&#xff08;Binary Lo…

python-tkinter程序的带包发布pyinstaller的使用闭坑

一、pyinstaller 不是内部或外部命令 1、安装pyinstaller 安装的时候一定注意别少了install后的er&#xff0c;pyinstaller,否则会安装成其他的程序的。安装的指令如下&#xff1a; pip install pyinstaller2、配置运行环境 首先找到你的python的安装目录&#xff0c;这个我…

Web APIs知识点讲解(阶段六)

BOM- 操作浏览器 一.Window对象 1. 依托 BOM 对象实现对历史、地址、浏览器信息的操作或获取2. 具备利用本地存储实现学生信息表案例的能力 1.BOM(浏览器对象模型) BOM(Browser Object Model ) 是浏览器对象模型 window 是浏览器内置中的全局对象&#xff0c;我们所学习的所…

论文笔记:基于多粒度信息融合的社交媒体多模态假新闻检测

整理了ICMR2023 Multi-modal Fake News Detection on Social Media via Multi-grained Information Fusion&#xff09;论文的阅读笔记 背景模型实验 背景 在假新闻检测领域&#xff0c;目前的方法主要集中在文本和视觉特征的集成上&#xff0c;但不能有效地利用细粒度和粗粒度…

C# 学习第五弹——语句

一、if语句 —简单if语句 —if else 语句 —if else if else 语句 1、简单if语句 if&#xff08;表达式&#xff09;{语句} &#xff08;1&#xff09;表达式必须使用圆括号括起来&#xff1b; &#xff08;2&#xff09;表达式&#xff1a;关系表达式或逻辑表达…

Express

可以方便、快速创建Web网站的服务器&#xff08;提供web网页资源&#xff09;或API接口服务器&#xff08;提供API接口&#xff09; app.get(请求URL&#xff0c;function(req,res)>{}) //req:请求对象&#xff08;包括请求属性和方法&#xff09; //res:响应对象&#xff…

【python】常用函数汇总(持续更新……)

文章目录 【numpy.exp()】返回e的幂次方&#xff0c;e是一个常数为2.71828【np.dot()】矩阵相乘【np.linalg.inv()】矩阵求逆 【numpy.exp()】返回e的幂次方&#xff0c;e是一个常数为2.71828 举例&#xff1a;numpy.exp() 【np.dot()】矩阵相乘 【要点】 1、前者的列数后者…

suno让音乐行业一夜变天,如何快速体验suno一键生成音乐?

随着AI的应用变广&#xff0c;各类AI程序已逐渐普及。AI已逐渐深入到人们的工作生活方方面面。而AI涉及的行业也越来越多&#xff0c;从最初的写作&#xff0c;到医疗教育&#xff0c;再到现在的音乐。 Suno是一个专业高质量的AI歌曲和音乐创作平台&#xff0c;用户只需输入简…

Vue ElementPlus Form、Form-item 表单

Form 表单 由输入框、选择器、单选框、多选框等控件组成&#xff0c;用以收集、校验、提交数据&#xff0c;组件升级采用了 flex 布局&#xff0c;以替代旧版本的 float 布局。 典型表单 包括各种表单项&#xff0c;比如输入框、选择器、开关、单选框、多选框等。 在 Form 组件…

打造高效安全的电池管理 | 基于ACM32 MCU的两轮车充电桩方案

前 言 随着城市化进程的加快、人们生活水平的提高和节能环保理念的普及&#xff0c;越来越多的人选择了电动车作为代步工具&#xff0c;而两轮电动车的出行半径较短&#xff0c;需要频繁充电&#xff0c;因此在城市中设置两轮车充电桩就非常有必要了。城市中的充电桩不仅能解决…

python实现模糊神经网络(pytorch版)

1 理论 模糊神经网络是一种基于模糊逻辑的神经网络模型&#xff0c;其主要用于处理模糊信息和不确定性的问题。模糊神经网络可以将输入数据映射到一个模糊集合中&#xff0c;然后通过一系列的模糊规则进行求解&#xff0c;最终输出一个模糊集合。 模糊神经网络的基本原理是将…