函数的缺省参数,函数重载与底层函数名修饰解释,引用的初步介绍

news2024/10/3 4:38:03

TIPS

  1. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。
  2. 在日常练习中,建议直接using namespace std即可,这样就很方便。
  3. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。
  4. 头文件在预处理阶段之后就已经默认在.c或.cpp文件上展开了。编译器在实际预处理完了之后就没有头文件什么事儿了,头文件的内容早已经全部展开了,要处理的都是.c.cpp。

函数的缺省参数(备胎)

  1. 缺省参数是声明或定义函数,是为函数的参数指定一个缺省值,然后再调用该函数时,如果说没有指定实参,那么就用形参的缺省值,否则就用指令的实参。
  2. 是在c语言当中闻所未闻的,C语言的语法当中,如果定义函数的时候,参数必须是一个数据类型加上一个形参名称,不可能再说去等于一个什么值,是在c++里面就可以,因为他语法上面更完善了,有了缺省参数这个含义,你实参没有传过来的时候,那我在函数里面的形参就用之前定义函数时设定的缺省值
  3. 缺省参数又可以分为全缺省参数与半缺省参数,全缺省参数就是说这个函数里面所有参数都是缺省参数在这里插入图片描述
  4. 半全省参数,就是说这个函数的参数部分从右往左连续依次为缺省参数,但在最开头一些就不是缺省参数,缺省参数的分布必须是从右向左依次来给出,从右向左连续,不能间隔着给。在这里插入图片描述
  5. 对于全缺省参数的函数,它的调用方式就非常多了,在调用是全缺省参数的函数的时候,假设这个函数的参数有五个,全部都是缺省参数,你传一个参数,那这个参数就是给第一个缺省参数的,你传两个参数,那么这两个参数是给头两个缺省参数的,那么这样子其他后面三个缺省参数在函数里面用的就是事先设置的缺省值,不能跳跃的传。
  6. 对于半缺省参数的话,都必须得从右往左缺省,这些都是规定死的。
  7. 函数里面的缺省参数设定的那个缺省值,只不过是个备胎而已,有实参肯定优先用实参
  8. 如果说一个函数有缺省参数,在函数的定义与声明当中,不能同时给缺省值。首先如果说你给的两个缺省值不一样,那就相当于有两个备胎,那么当你没有实参传进来的时候,我到底该用哪个缺省值呢?其次就算是你给了两个缺省值,这两个缺省值是一样的,编译器还是会报错的。所以说不要同时去给。一般推荐的写法是在声明当中去给,函数具体定义的时候就不要去给了,倒过来也不可以。在这里插入图片描述
  9. 然后具体说到这个缺省值,也就是这个备胎的话,一般都是用常量与全局变量去充当缺省值。一般情况下都不会用全局变量,就用常量
  10. 反正对于缺省参数,只要记住,因为我们在写大一点的项目的时候,函数的声明与定义肯定是分开写的。然后这时候如果说你在这个函数参数里面想要使用缺省参数的话,这个缺省值在函数声明的时候去给啊,不要到函数具体定义的时候。更加不能两种情况同时给缺省值。

程序环境与预处理的复习回顾

  1. 在这边必须得不得不把程序的编译链接等知识回顾一下。程序运行的环境分为翻译环境与执行环境。在这个翻译环境之下,又分为编译加链接。然后这个编译的过程又分为预处理,编译,汇编。当我们把源代码test.c写完之后,我们的这个代码首先需要经过预处理阶段,这个预处理阶段主要干的是:删除注释,处理预处理指令,处理预定义符号,这都是一些文本操作,主要是替换与删除之类,诸如#include #define 等等都是在这个时候被处理掉的。然后这时候可以用gcc命令生成xxx.i文件;预处理完了之后,接下来就是编译环节,这个环节主要就是把预处理完之后的源代码给他翻译成汇编代码,在这个过程当中有很多细节,其中有一点是符号汇总:所谓的符号就是具有全局性质的变量名与函数名,在这个过程当中会把这些符号给他汇总记录下来,在这个过程当中会默认生成xxx.s文件。接下来是汇编阶段:汇编阶段就是把翻译好的汇编代码首先给他分成一段一段,然后变成可以执行的机器指令,也就是二进制指令,这时候会默认生成xxx.obj目标文件,当然在这个过程当中也会有很多的细节,其中一个就是形成符号表,相当于就是把那些符号与这些符号所对应的地址给联系结合起来,如果说地址暂时找不到,那就是随便一个随机值。此时编译阶段已经完成,上述的这个阶段每一个.c文件都是单独的经过编译器去编译生成单独的一个.obj文件,各个之间互不干涉,互不影响,各自都独立完成。接下来是链接阶段,这时候我已经生成了这么多个互相独立的obj文件,是时候该把它整合起来了,链接阶段分为合并段表,符号表的合并与重定义,在这个过程当中,首先把之前生成的obj文件里面的所有二进制指令一段一段的嘛,先给他全部合成起来,然后把符号表也全部合成起来,与此同时,如果有地址随机值的话就用正确的地址去代替随机值。最后把这么多的.obj目标文件全部整合成一整个.exe文件,就是可执行程序,里面全部都是二进制代码,计算机能够看懂,我们看不懂。在这里插入图片描述
    在这里插入图片描述

同作用域内的函数重载

  1. c语言当中不允许同名函数的存在,也就是说函数名不能相同。什么地方我们就会用这些所谓的同名函数呢?比如说现在我有两个add函数,一个函数实现两个整形的相加,另一个函数实现两个浮点数的相加。在c语言当中,这两个函数不能够都叫做add,必须得以示区分:比如addi, addd。但这样子就会非常的不方便。然后在c++当中就增加了一个新的东西,叫做函数重载。
  2. 函数重载,它允许不同函数函数名相同,但是他有另外一个要求,必须参数不同。那什么叫做参数不同呢?包含三个方面:一个是参数个数不同,还有一个是参数类型不同,还有一个是类型顺序不同。
  3. 函数重载,也就意味着相同名字的函数,我在代码当中具体定义了不止一个。那当具体在main函数里面的这么一个名字的函数在实现的时候用哪个定义实现呢?它是会自动识别类型。
  4. 反正函数重载,尽管这些函数定义它们的函数名称都是相同的,但是要么是参数数量不同,要么是参数类型之间不同,要么是参数类型的顺序不同,反正你在具体传入实参的时候,编译器会根据你传入的参数的类型,数量与顺序等,去自动识别到底是去用哪个函数定义去实现
  5. 函数重载是指允许在同一作用域中声明几个功能类似的同名函数。注意,必须是在同一作用域当中。比如说都定义在全局域中。说都定义在同一个命名空间域当中。比如说你们在不同的命名空间当中,那就不行了,就不是重载了,必须在同一个作用域当中,然后函数名称可以相同,但是必须是参数的个数,类型或类型顺序不同。
    在这里插入图片描述

同作用域内函数重载的底层解释(函数名称修饰)

  1. 那这样子会不会相较于c语言而言变慢了呢?实际上不会。如果说你是在运行的时候再去匹配类型,这样一折腾肯定会变慢。但刚才这个匹配同名函数的过程实际上并不是在运行阶段完成。而是在编译的时候就已经匹配完成了,编译过后整个代码实际上都是一个二进制指令了,函数调用代码实际上也会变成call 函数地址。所以说如果说放慢的话***,也应该是编译速度变慢了一点,与运行没有任何一点关系***。而编译的速度我们也根本就不是很在乎。
  2. 那接下来就深挖底层:编译的时候这个编译器是怎么指定或识别是哪个同名函数的呢?C++其实相对于c语言是做了很多处理的。这个不一样的处理就是c++对函数名进行了一些修饰。
  3. 首先我们需要知道的是和我们之前学的函数栈帧,程序环境与预处理的知识,当在编译完成就是说把代码给他转换为汇编代码的时候,这个add函数转化为汇编代码,其实就是一条call指令,call的后面就是这个add函数的地址(我们现在讨论的是汇编代码这个层次)。然后代码执行到这边的时候就会去跳到地址所在的地方,然后接下来到那个调用函数里面,在一开始我们都知道就又是创建函数栈帧啊等等,所以说函数的地址本质上最后走走走走过去就是一串指令,因此说函数的地址本质上就是说函数里面一堆指令当中第一句指令的地址(我这边说的指令不是可执行的机器指令,我这边说的指令也不是二进制指令,我现在说的是汇编代码)。
    在这里插入图片描述
    在这里插入图片描述
  4. 那然后我们现在言归正传,比如说现在我有两个add函数(当然是参数不同的喽),那然后到时候转到汇编代码的时候,他是怎么去知道call后面跟哪个add函数的地址的呢?其实呢c++里面做了一个事情叫做函数名修饰规则
  5. c的话是区分不了的,在c语言当中,它直接通过函数名去找地址,然后在c++这边的话,他把这个函数名给他修饰了一下,然后具体修饰的规则的话,不同的平台上也会有不同,我们这边VS 2019还比较麻烦与复杂,在Linux环境下,***这个函数名修饰成这样:_Z(这个是规定的前缀)3(这个函数的长度)add(函数名)ii(他这边取的是这个参数类型的首字母),***因此尽管你在肉眼上看到两个一模一样函数名的函数,但实际上经过它内部的修饰的话,这两个函数的函数名在内部还是不同的,那么因此也有他各自的一个地址。
  6. 比如说_ZAdd3ii有他自己的地址;_ZAdddd也有他自己的地址。然后我到时候去寻找地址的时候我是拿修饰后的函数名去寻找,修饰的规则还是一样的,然后就这么拿着个修饰后的名字去找,然后找到地址,找到了之后我就放到call的后面。C话是直接用纯裸的名字去找,然后如果说你有两个函数名称相同的话,那不就直接over了吗?会冲突。
  7. 所以说再回到之前,如果说你的参数个数不同,你的类型不同,你的类型之间顺序不同,都会对修饰后的函数名产生影响,修饰后的名字不同,那么去找的时候就能够找到对应的真正的对象,就可以很好的以示区分了。在这里插入图片描述
    在这里插入图片描述
  8. 如果说两个函数函数名是一样,这个就不多说了,然后类型数量,类型名称,类型前后顺序也一样,但就是返回值不一样,那请问这时候能不能构成函数重载的呢?那这肯定是不能的。那为什么不能呢?有些人说在函数修饰的规则里面,在对函数名修饰的时候,没有把返回值给他纳入进去,这个回答大错特错,就算我把函数的返回值也纳入进去,还是不能够进行函数重载,为什么呢?是这样的,我在转汇编的时候,进行修饰函数名的时候,即使我把函数的返回类型也纳入进修饰过程当中,但我刚在调用函数的时候,比如说前后两行代码为add(1,2)与add(1,2),那我在这时候我在进行修饰的时候,我该怎么区分呢?对于这个返回值根本就无能为力我怎么知道你到底是要调谁呢?到底是有返回值的那个还是没有返回值的那个呢?所以说不是因为函数名修饰规则没有带返回值,而是因为调用的时候只写了参数,没办法区分。
  9. 再举个例子,比如说int func1(int*p, double x);这个函数的修饰后函数名是_Z5func1Pid

在这里插入图片描述
这样子也不能算函数重载,反正函数重载的话,一定要从底层函数名修饰的角度去理解

引用的简单介绍 (内存数据类型)& 外号 = 变量名or已有外号 一定要从内存角度理解

  1. 应用其实是补了c语言当中指针的大坑。指针确实在有些场景之下让人用起来很不舒服。
  2. 引用就是取别名,诸如蛋哥。首先我们必须得谈一下正常的赋值操作。比如说现在有个变量a是4,然后我又新去创建了一个变量b,然后b=a。这种就是赋值操作,就是说我又先去创建了一个四个字节的一块内存空间,然后把a里面的数据给他拷贝给到了b,就是这么一个正常的赋值操作。
  3. 然后这边还得插一句:C++里面有很多符号是共用的,比如说到目前为止我们已经知道至少<<就是共用的,这个不仅仅是移位运算,这还是一个流插入符号,也叫作输出。然后这边在引用过程中,也出现了符号共用的现象。
  4. &C语言当中它只有一个含义,就是取地址的意思。引用就是取别名的意思。比如说这一串代码的意思就是:int i = 3,int& k = i。我们已经知道有一个变量i,而它的值是3,我给这块空间又取了一个名字叫做k,k就是i,i就是k。注意:如果&放在类型后面(如:int&),那就是引用。然后这个符号&如果放在变量的前面,那就是地址的意思
  5. int& k=i,你只需要记牢:k就是i,i就是k(但两者只是同一块内存空间的不同名字而已)。这个需要与赋值进行区分,你把赋值赋给一个变量之后,这两个变量本质上是两块各自独立的不同内存空间,但这边引用的话只是同一块内存空间的两个不同的名称而已。然后可以对他本身再取一个别名,然后你也可以对别名再取别名。反正也只要记牢,是具有传递性的。
  6. 你说我有一个交换两个数的这么一个swap函数,我在往swap函数里面传入参数的时候,肯定不能传数值,肯定需要传这两个数的地址。一定要传址调用,因为不然的话,形参的改变不会影响实参。在c语言的背景之下,需要用指针去解决。在c++里面就不一定必须要用指针。注意:引用的一个最核心的特质就在于:空间不变,空间不变,空间不变,空间不变,只是同一块空间用了两个不同的称呼而已。相较于c语言那种指针的方式就是看起来更稍微舒服一点。

在这里插入图片描述
8. 所以在函数参数里面用引用,他的作用就凸显出来了。然后任何类型都可以取别名,你也可以对指针进行取别名,(那块空间的数据类 型)&别名。引用的作用就是如果说你希望形参的改变能够影响实参,在c当中你必须传地址,这是毋庸置疑的。是在c++当中,可以不用去传 地址,直接引用一下,直接原原本本原空间空降到这个函数里面。(一定要从内存的角度去理解)

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

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

相关文章

ReetrantLock源码剖析_03公平锁、非公平锁

一直努力就会有offer,一直努力就会有offer,一直努力就会有offer! 文章目录 ReetrantLock公平锁代码解析ReetrantLock公平锁执行流程ReetrantLock非公平锁代码解析ReetrantLock非公平锁执行流程公平锁与非公平锁的比较 ReetrantLock公平锁代码…

前端部署发布项目后,如何通知用户刷新页面、清除缓存

以下只是一些思路,有更好的实现方式可以留言一起交流学习 方式一:纯前端 在每次发布前端时,使用webpack构建命令生成一个json文件,json中写个随机生成的一个字符串(比如时间戳),每次打包程序都…

【Python入门第五十天】Python丨NumPy 数组搜索

搜索数组 可以在数组中搜索(检索)某个值,然后返回获得匹配的索引。 要搜索数组,请使用 where() 方法。 实例 查找值为 4 的索引: import numpy as nparr np.array([1, 2, 3, 4, 5, 4, 4])x np.where(arr 4)pri…

node可以用nvm快速切换版本,golang如何快速切换版本?用gvm就行。

使用 gvm 可以带来以下好处: 快速切换 Golang 版本,方便进行版本测试和开发;可以在多个项目中同时使用不同版本的 Golang 包和工具,避免冲突;可以通过 gvm 管理不同版本的 Golang,方便安装、卸载和更新&am…

STL--vector

一、vector介绍 vector是表示大小可以更改的数组的序列容器 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而…

移动端屏幕适配

文章目录 移动端屏幕适配移动端屏幕适配和响应式布局区别基本知识简单屏幕适配 移动端屏幕适配 移动端屏幕适配和响应式布局区别 移动端适配响应式布局终端移动端PC端和移动端常用单位宽高:rem 或 %字体:px宽:%高、字体:px宽高宽…

Docker网络模式与cgroups资源控制

目录 1.docker网络模式原理 2.端口映射 3.Docker网络模式(41种) 1.查看docker网络列表 2.网络模式详解 4.Docker cgroups资源控制 1.CPU资源控制 2.对内存使用的限制 3.对磁盘IO的配置控制(blkio)的限制 4.清除docker占用…

Vue3教程

文章目录 参考资料1 setup语法糖1.1 vue2中的写法1.2 setup语法糖在vue3中使用 2 ref reactive 事件2.1 ref2.2 reactive2.3 事件:在setup script中,直接定义事件,不需要像vue2那样在method中定义 3 computed & watch & watchEffect3…

详解DHCP和DNS实验汇总

文章目录 1.实验说明2.实验步骤2.1(linux的CentOS 7-2)命令配置2.2 (linux的CentOS 7-3)命令配置2.3 客户端(WIN10)命令配置2.4 客户端(CentOS 7-1)命令配置 1.实验说明 实验要求:要求在一台主机中同时配置DNS服务器和…

【SpringCloud】1、Nacos注册中心、配置中心搭建

1、Nacos 简介 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助…

PartiQL 对 SQL 的扩展,可以查询非结构化的数据

目录 开始 先决条件 下载 PartiQL CLI 运行 PartiQL CLI 窗户 macOS (Mac) 和 Unix 命令行教程 介绍 PartiQL 查询与 SQL 兼容 PartiQL 数据模型:许多底层数据存储格式的抽象 了解更多信息 查询嵌套数据 嵌套集合 取消嵌套嵌套…

Hbase数据库完全分布式搭建以及java中操作Hbase

文章目录 1.基础的环境准备2.完全分布式 Fully-distributed2.1 配置文件hase-env.sh2.2 hbase-site.xml2.3 配置regionservers2.4 配置备用的master2.5 HDFS客户端配置2.6 启动2.7 通过页面查看节点信息 3. java中客户端操作Hbase3.1 引入依赖3.2 初始化创建连接3.3 操作Hbase数…

Qt/QML编程学习之心得:D-BUS进程间通信(四)

Qt/QML应用编程最适合于一些触摸的嵌入式界面设计,那么GUI界面怎么与底层的设备通信,怎么与一个系统内其他模块通信的呢?这就不得不说一个很重要的设计模式:d-bus。 D-BUS是一个系统中消息总线,用于IPC/RPC。消息系统…

vi编辑器的使用介绍

vi编辑器的使用 vi的特点与运用场景vi的使用简易执行一个案例按键说明第一部分:命令模式的按键说明(光标移动、复制粘贴、查找替换)移动光标的方法查找与替换删除、复制与粘贴 第二部分:命令模式切换到输入模式的可以按键进入插入或替换的编辑模式 第三部…

A100 Jeston TX1/TX2使用教程-介绍

大家好,我是虎哥,经过一段时间的整理,终于完成了我自己算力盒子,A100系统的设计和研发,今天就来和大家聊聊这款针对TX1和TX2的入门级计算盒子的一些特性和功能。 一、EdgeBox_Umate_A100 算力盒子 A100 算力盒子是“玩…

系统集成项目管理工程师 笔记(第五章:项目立项管理)

文章目录 5.1 项目建议 2225.2 项目可行性分析 224项目可行性研究内容:5.2.2 项目可行性研究阶段 227 5.4 项目招投标 229《中华人民共和国招标投标法实施条例》5.4.1 项目招标 2295.4.2 项目投标 2305.4.3 开标与评标 2345.4.4 选定项目承建方 235 5.5 项目合同谈判…

实模式下内存访问

虽然有了寄存器,但是数据和指令还是需要存储到内存中。通常情况下需要把数据从内存中放到寄存器中才能使用,同样的指令需要放到寄存器中才能被CPU执行。 所有的内存访问都需要段寄存器左移四位加上其他寄存器的值才能得到真正地址值。这是由于以前运行实…

Unity使用ShaderGragh制作透明指针

Unity使用ShaderGragh制作透明指针 1 概述2 使用环境3 制作流程3.1 创建一个ShaderGragh3.2 打开ShaderGraph编辑器3.3 编辑器界面介绍3.4 Shader节点和部分信息如下3.5 常用节点介绍3.6 使用Shader3.7 贴图规范 4 控制Shader旋转4.1 API介绍4.2 示例代码:3.9 Shade…

Redis 6.0+ 的 ACL 机制

目录 前言一、安装 Redis 服务二、创建 ACL 用户三、用户密码管理3.1 删除密码3.2 重置用户和密码 四、权限管理4.1 key 管理4.2 权限管理 五、ACL 用户存储5.1 配置文件实现5.2 外部 ACL 文件实现 前言 Redis 6.0 引入了 ACL 机制,类似 MySQL 一样全部权限管理&am…

表单验证:自定义校验规则

Element UI 为我们提供了表单校验规则&#xff0c;但业务需要&#xff0c;我们常常要自定义校验规则 需求 实现表单中一个输入框&#xff0c;不能输入大于30的数字 思路 hrml&#xff1a; 自定义校验规则&#xff1a; 约定的校验规则&#xff1a; 代码 <template&g…