我的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. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型面试准备(九):简单透彻理解MoE

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何备战、面试常考点分享等热门话题进行了深入的讨论。 合集在这…

Linux(CentOS7)安装 MySQL8

目录 下载 上传 解压 创建配置文件 初始化 MySQL 服务 启动 MySQL 服务 连接 MySQL 创建软链接 下载 官方地址&#xff1a; MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/选择版本前需先看一下服务器的 glibc 版本 ldd --versio…

浏览器工作原理与实践--垃圾回收:垃圾数据是如何自动回收的

在上一篇文章中&#xff0c;我们提到了JavaScript中的数据是如何存储的&#xff0c;并通过例子分析了原始数据类型是存储在栈空间中的&#xff0c;引用类型的数据是存储在堆空间中的。通过这种分配方式&#xff0c;我们解决了数据的内存分配的问题。 不过有些数据被使用之后&am…

CVE-2023-38408漏洞修复 - 升级openssl和openssh

CVE-2023-38408 OpenSSH 代码问题漏洞修复 - 升级openssl和openssh ※ 重要说明&#xff1a; 1、升级后会导致无法用ssh远程登录&#xff0c;提示“Permission denied, please try again.” 2、解决方案请查看本章节【三、解决升级后无法用ssh远程登录】 目录 CVE-2023-38408 O…

WebScraper网页数据爬取可视化工具使用(无需编码)

前言 Web Scraper 是一个浏览器扩展&#xff0c;可以实现无需编码即可爬取网页上的数据。只需按照规则进行配置&#xff0c;即可实现一键爬取导出数据。 安装 进入Google应用商店安装此插件&#xff0c;安装步骤如下&#xff1a; 进入Google应用商店需要外网VPN才能访问&…

微服务demo(三)nacosfeign

一、feign使用 1、集成方法 1.1、pom consumer添加依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.6.RELEASE</version></dependency&…

WEB APIS知识点案例总结

随机点名案例 业务分析: 点击开始按钮随机抽取数组中的一个数据,放到页面中点击结束按钮删除数组当前抽取的一个数据当抽取到最后一个数据的时候,两个按钮同时禁用(只剩最后一个数据不用抽了) 核心:利用定时器快速展示,停止定时器结束展示 <!DOCTYPE html> <html…

智慧公厕产品的特点、应用场景

随着城市化进程的加速和智能科技的不断发展&#xff0c;智慧公厕作为城市管理的重要组成部分&#xff0c;逐渐成为了现代城市的一道靓丽风景线。它的特点和应用场景备受人们关注和喜爱。 智慧公厕的特点有哪些呢&#xff1f;首先&#xff0c;它智能化的设备和感应技术为其特点…

《操作系统导论》第14章读书笔记:插叙:内存操作API

《操作系统导论》第14章读书笔记&#xff1a;插叙&#xff1a;内存操作API —— 杭州 2024-03-30 夜 文章目录 《操作系统导论》第14章读书笔记&#xff1a;插叙&#xff1a;内存操作API1.内存类型1.1.栈内存&#xff1a;它的申请和释放操作是编译器来隐式管理的&#xff0c;所…

Xcode删除原本的Git,再添加新的git

本文参考&#xff1a;Xcode怎么删除原本git,在重新设置新的git地址_ios xcode 删除原本git-CSDN博客 开发中会有一个问题。Xcode项目A 提交到Git服务器server1&#xff0c;此时项目A内部已经存在一个Git文件&#xff0c;与server1相关联。 此时你想将项目A提交到 另一个Git…

前端实现菜单搜索搜索(功能模版)

目录 前言正文 前言 总体界面如下所示&#xff1a; 正文 <template><div class"avue-searchs"click.self"handleEsc"><div class"avue-searchs__title">菜单搜索</div><div class"avue-searchs__content"…

PS从入门到精通视频各类教程整理全集,包含素材、作业等(4)复发

PS从入门到精通视频各类教程整理全集&#xff0c;包含素材、作业等 最新PS以及插件合集&#xff0c;可在我以往文章中找到 由于阿里云盘有分享次受限制和文件大小限制&#xff0c;今天先分享到这里&#xff0c;后续持续更新 PS人物数码照片处理技法视频教程 https://www.al…

Mybatis项目运行成功但是返回的数据是引用的地址

如图所示&#xff1a; 解决方法&#xff1a;是因为在实体类当中没有重写toString方法 成功输出&#xff1a;

企微获客助手到底有哪些价值?

获客助手作为企业微信官方提供的获客工具&#xff0c;在私域布局中确实展现了其强大的引流效率和便利性。这一工具通过简化传统引流过程中的复杂步骤&#xff0c;使得企业能够更高效地吸引和转化潜在客户。此外&#xff0c;获客助手还能实现不同渠道的无缝链接&#xff0c;进一…

脑机辅助推导算法

目录 一&#xff0c;背景 二&#xff0c;华容道中道 1&#xff0c;问题 2&#xff0c;告诉脑机如何编码一个正方形格子 3&#xff0c;让脑机汇总信息 4&#xff0c;观察图&#xff0c;得到启发式算法 5&#xff0c;根据启发式算法求出具体解 6&#xff0c;可视化 一&am…

C++ 项目:使用 GSL 数学运算库 C++ 调用Python

文章目录 Part.I IntroductionChap.I CMakeListsChap.II ExportLibGSL.hChap.III test_python.cpp Part.II GSL 使用方法Part.III C 调用 Python 使用方法相关博客 Part.I Introduction 本项目是一个使用 GSL 的小项目&#xff0c;还有 C 调用 Python。项目虽简单&#xff0c;…

【研发日记】Matlab/Simulink开箱报告(十一)——Requirements Toolbox

目录 前言 Requirements Toolbox 编写需求 需求联接设计 需求跟踪开发进度 追溯性矩阵 分析和应用 总结 前言 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;六&#xff09;——S-Fuction模块&#xff08;TLC&#xff09;》 见《开箱报告&#x…

挑战从0开始开发仿小红书app第一天

00后挑战从0开始开发仿小红书|Day01 前言 记录开发的全阶段&#xff0c;比如需求分析&#xff0c;开发过程&#xff0c;测试阶段。不懂代码没关系&#xff0c;可以看看互联网岗位在干嘛 会使用一些辅助编码工具&#xff0c;就是类似于ChatGPT的大模型&#xff0c;比如通义灵码…

java文件File和IO流(一)-- File文件,IO流,缓冲流,字节流,字符流

File文件操作类 java.io.File类&#xff1a;文件和文件目录路径的抽象表示形式&#xff0c;与平台无关 File 能新建、删除、重命名文件和目录&#xff0c;但File 不能访问文件内容本身。如果需要访问文件内容本身&#xff0c;则需要使用输入/输出流。 想要在Java程序中表示一个…

C语言数组详解

一维数组 创建和初始化 数组就是一组相同元素的集合。 他的创建&#xff1a; char arr[10]; int arr1[5]; 数组创建中 [] 里不能是变量&#xff0c;但是在c99标准之后就可以了被称为变长数组&#xff0c;但是不常用&#xff0c;而且变长数组不能初始化。 初始化&#xff…