C++详细分析引用的使用及其底层原理

目录
  • 引用
  • 引用的注意事项
    • 引用做参数
    • 实参传值和传引用的优劣
  • 引用做函数返回值
  • 传引用返回
  • 引用的权限
  • 引用经典笔试题
  • 产生临时变量的情况
  • 关于右值
  • 引用的底层原理

引用

引用不是定义一个新变量,而是给已存在的变量取了一个外号,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

举个形象的例子,鲁智深又被叫做"花和尚",这里的花和尚和鲁智深都是同一个人,花和尚就是鲁智深的引用,说白了引用其实就是取外号。

引用的注意事项

  • 1.引用必须初始化
int main()
{
int a;
int &b =a;
}

这时b就是a的外号。
下面的这种没有初始化的做法是错误的

int main()
{
int &b;//引用必须初始化,这里没有进行初始化。
}
  • 2.一个变量可以有多个引用

在生活中,我们可能有多个名字,在家中父母可能叫你小名,在外面别人可能叫你的全名或者外号

其实也就是一个变量可以有多个外号,也就是可以有多个引用。

int main()
{
int a;
int &b=a;
int &c=a;
}

这里的b和c都是a的外号,b,c,a三个变量指向的都是同一块内存空间。

  • 3.引用一旦引用了一个实体就不能再引用其他的实体

形象的来说,就是你和你的亲弟弟不能用同一个名字

下图c是a的引用,那么现在他就不能做b的引用了。

引用做参数

在讲引用做参数之前,我们先需要了解一下,参数传值和参数传引用的区别。

1.参数传值

下图实参传递了a,形参b对其进行了接收并修改,但是实参a最终并没有受到影响,这又是为什么呢?

原来,参数传值的时候,形参会生成一份实参数据的拷贝,也就是说实参和形参指向的不是同一块空间,所以形参的修改不会影响实参。

2.引用传参

下图进行了引用传参,形参的修改对实参产生了影响,我们可以大胆推测形参和实参是一块空间。

原来,引用传参的时候,形参不再是实参的拷贝,而是实参的一个引用,也就是说实参和形参指向的是同一块内存空间,形参的改变会影响实参。

实参传值和传引用的优劣

1.实参传值:

缺点:形参会生成一份实参数据的拷贝,当数据量很大时,在一定程度上就会影响程序的运行速度

优点:因为形参是实参的拷贝,所以形参的操作不会影响实参,可以防止实参数据遭到污染。

2.实参传引用

缺点:形参的操作对实参会产生影响,形参的错误操作会让实参数据遭到修改。

优点:因为形参是实参的引用,一定程序上,可以提高程序的运行速度

引用做函数返回值

在了解引用做返回值时,我们还是得先了解传值返回和传引用返回的区别。其实原理和上面大致相同。

1.传值返回

在返回c的时候,返回的不是c的本体,而是将c拷贝在一块临时空间里,所以返回的其实是这块临时空间。然后ret再次拷贝一个和这块临时空间一样数据的空间。

这块有点像俄罗斯套娃,需要多画图理解。我一开始也有点懵逼。多画图就清晰了。

但是问题又来了,这块临时拷贝空间又存储在哪里呢?

当c比较小的时候(4字节或者8字节),一般是存储在寄存器中。

当c比较大的时候,临时变量放在该函数的栈帧上面。

接下来我们通过观察代码的反汇编进行证明:

分析这段代码的汇编,在进入add函数以后,先是将a的值给了eax,然后将b的值加上a,接着将eax里的值给了c。最后对c进行返回,在返回c的时候生成一个临时拷贝,c将自己的值又给到了寄存器eax中。

最后回到主函数,eax将值给了ret。

传引用返回

这里进行的是传引用返回,也就是说ret其实就是c的别名。传引用返回,返回的就是本体,而不是拷贝。因为这里c是一个局部变量,在函数结束以后,栈帧被销毁,局部变量的空间被系统回收了。这时ret再去访问c的内容就可能造成非法访问,并且c的值可能已经被修改了。

形象的来说:就是你原先买了一个房子,后面你又将其卖给了别人,后面你想再次进入这个房子,但是这间房子已经不属于你了,你进房子的操作就属于非法访问了。

所以,传引用返回时,返回的对象不能是出函数就被系统回收的。也就是说返回的变量不能是一个局部变量。

引用的权限

1.引用的权限可以缩小

int main() {
int a = 10;
const int &b = a;//权限的缩小
}

这里变量a是可读可写的,而b是a的引用,b只能对a这块内存空间进行读取,不能进行修改,这就是权限的缩小,这在C++中是可以的。

2.引用的权限不能放大

int main() {
const int a = 10;
int &b = a;//权限的缩小
}

这里变量a指向的空间是只能读取的,不能进行修改,而a的引用b,是可以对a指向的这块空间进行修改的,使得权限得到了放大,这种语法在C++中是错误的。

总结:引用可以进行权限的缩小,但是不能进行权限的放大

引用经典笔试题

下图中的代码(1)和(2)是否能够正常运行?

double d=11.1;
int a=d;(1)
int &ret=d;(2)

答案:(1)可以运行通过,(2)不行。

代码(1)是普通的隐式类型转换。

而在了解代码(2)的错误原因之前,我们需要回顾一些知识:

产生临时变量的情况

1.类型转换

double d=11.1;

int a=d;

d的类型是double,a的类型是int,类型不同,正如下图所示,在发生隐式类型转换的时候,需要将d的值存到一个int类型的临时变量里,然后将这个临时变量的值赋予给a。

2.整形提升

int a=10

char c=‘b’;

if(a>c){

}

这里并不是拿c直接和a进行比较,而是将c赋值到一个int的临时变量里,通过这个临时变量去和a进行比较。

关于右值

结论:右值是具有常性的,是不可修改的。

这里的右值不能通过字面意思(处于式子右边的值)进行理解,以下的几种情况一般都是属于右值。

1.表达式的计算结果: 如:5+3=8,这里的8就是右值。

2.常量:如a=5,这里的5就是右值。

3.一些隐式类型转换产生的临时变量。如

int a;

double d;

d=a;

这里隐式类型转换产生的临时变量也是一种右值。

在明白了这些基础原理以后,我们开始学习为什么代码int &ret=d;(2)是错误的。

这里引用的变量其实是一块临时空间,而临时空间是右值是不能修改的,这种引用的方式本质上其实就是权限的放大,因此编译不能通过。

引用的底层原理

int main() {
int a;
int &b = a;
int *p = &a;
}

这里通过调试模式观看这段代码的反汇编。

这里的lea是取地址的意思。 从汇编代码可以看出,在底成实现的时候,引用和指针的实现方式是一样的,所以说: 引用的底层是通过指针实现的

到此这篇关于C++详细分析引用的使用及其底层原理的文章就介绍到这了,更多相关C++引用的使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++简明分析讲解布尔类型及引用

    目录 一.C++中的布尔类型 二.C++中的三目运算符 三.C++中的引用 四.总结 一.C++中的布尔类型 C++在C语言的基本类型系统之上增加了bool C++中的bool可取的值只有true和 false 理论上bool只占用一个字节 C++编译器会将非0值转换为true ,0值转换为false 注意: true代表真值,编译器内部用1来表示 false代表非真值,编译器内部用0来表示 下面看一下这段代码,加深一下对bool类型的理解. #include <stdio.h> int ma

  • 带你了解C++初阶之引用

    目录 一. 引用概念 二. 引用特性 三. 常引用 四. 使用场景 1.做参数 指针 引用 2.做返回值 2.1.传值返回 2.2.传引用返回 小结引用做返回值 五.函数参数及返回值 —— 传值.传引用效率比较 六. 引用和指针的区别 1.语法概念 2.底层实现 总结 一. 引用概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,语法理解上程序不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"

  • C++深入探究引用的本质与意义

    目录 一.引用的意义 二.特殊的引用 三.引用是否占用存储空间 四.引用的本质 五.引用的注意事项 六.小结 一.引用的意义 引用作为变量别名而存在,因此在一些场合可以代替指针 引用相对于指针来说具有更好的可读性和实用性 下面通过代码来进行说明,在C语言中,可以这么写: #include <stdio.h> void swap(int* a, int* b) { int t = *a; *a = *b; *b = t; } int main() { int a = 1; int b = 2;

  • C++引用和结构体介绍

    目录 文章转自微信公众号:Coder梁(ID:Coder_LT) 结构体是我们自定义的复合类型,本质上也是一种变量类型,所以一样可以使用引用.传递结构体引用的方式和其他变量一样: struct P { int x, y; }; void set_axis(P& a, P& b); 前文C++引用的使用与const修饰符当中也曾说过,虽然引用在基本类型上一样适用,但一般在实际使用当中,不在基本变量类型上使用引用.倒不是有什么问题,而是没有必要,毕竟基本变量类型占据的内存太小了,值传递和引用传

  • C++引用的使用与const修饰符

    目录 1.引用 2.函数引用传递 3.引用与const 4.const修饰符的优点 1.引用 引用是给已经定义的变量一个别名,可以简单理解成同一个变量的昵称.既然是昵称或者是别名,显然它和原本的变量名有着同样的效力.所以我们对别名进行修改,原本的变量值也一样会发生变化. 我们通过符号&来表明引用, 比如下面这个例子,我们创建了a变量的一个引用b int a = 3; int &b = a; b++; cout << a << endl; 由于b是a的一个引用,本质上

  • C++引用和指针的区别你知道吗

    目录 引用 1.引用概念 2.格式 3.引用特性 4.常引用 1.const引用 5.使用场景 1.引用作为参数 2. 引用作为做返回值 6.引用和指针的区别 7.引用和指针的不同点: 总结 引用 1.引用概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风 2.格式 类型& 引用变量名(对象名) = 引用实体: 例: void TestRe

  • C++中引用的相关知识点小结

    目录 引用的概念 引用特性 常引用 使用场景 引用和指针的区别 总结 引用的概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风".那么这里的“铁牛”.“黑旋风”就称李逵的引用. 在程序中呢,引用的用法如下: 类型& 引用变量名(对象名) = 引用实体: 举个例子: void TestRef() { int a = 10; int&

  • C++详细分析引用的使用及其底层原理

    目录 引用 引用的注意事项 引用做参数 实参传值和传引用的优劣 引用做函数返回值 传引用返回 引用的权限 引用经典笔试题 产生临时变量的情况 关于右值 引用的底层原理 引用 引用不是定义一个新变量,而是给已存在的变量取了一个外号,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 举个形象的例子,鲁智深又被叫做"花和尚",这里的花和尚和鲁智深都是同一个人,花和尚就是鲁智深的引用,说白了引用其实就是取外号. 引用的注意事项 1.引用必须初始化 int main() {

  • 一文彻底搞懂IO底层原理

    目录 一.混乱的 IO 概念 二.用户空间和内核空间 三.IO模型 3.1.BIO(Blocking IO) 3.2."C10K"问题 3.3.NIO非阻塞模型 3.4.IO多路复用模型 3.4.1.select() 3.4.2.poll() 3.4.3.epoll() 四.同步.异步 五.总结 一.混乱的 IO 概念 IO是Input和Output的缩写,即输入和输出.广义上的围绕计算机的输入输出有很多:鼠标.键盘.扫描仪等等.而我们今天要探讨的是在计算机里面,主要是作用在内存.网卡

  • C++详细分析讲解引用的概念与使用

    目录 1.引用的概念 2.引用的格式 3.引用的特性 4.取别名原则 5.引用的使用场景 做参数 做返回值 int&Count()的讲解 传值传引用效率比较 6.引用和指针的不同点 1.引用的概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 2.引用的格式 类型 & 引用变量名 ( 对象名 ) = 引用实体: 举例如下: 注意:引用类型必须和引用实体是同种类型的 3.引用的特性 (1). 引用在 定义时必须初

  • 详细分析Python垃圾回收机制

    引入 为什么要有垃圾回收机制 Python中的垃圾回收机制简称(GC),我们在程序的运行中会产生大量的变量用于保存数据,而有时候有些变量已经没有用了就需要被清理释放掉该变量所占据的内存空间.在一些较为低级的语言中(比如:C语言,汇编语言)对于内存空间的释放是需要编程人员来手动进行的,这种与底层硬件直接打交道的操作是十分的危险与繁琐的,而基于C语言开发而来的Python为了解决掉这种顾虑则自带了一种垃圾回收机制,从而让开发人员不必过分担心内存的使用情况而可以全身心的投入到开发中去. >>>

  • 详细分析Java内存模型

    目录 一.为什么要学习并发编程 二.为什么需要并发编程 三.从物理机中得到启发 四.Java 内存模型 五.原子性 5.1.什么是原子性 5.2.如何保证原子性 六.可见性 6.1.什么是可见性 6.2.如何保证可见性 七.有序性 7.1.什么是有序性 7.2.如何保证有序性 一.为什么要学习并发编程 对于 "我们为什么要学习并发编程?" 这个问题,就好比 "我们为什么要学习政治?" 一样,我们(至少作为学生党是这样)平常很少接触到,然后背了一堆 "正确且

  • Java详细分析String类与StringBuffer和StringBuilder的使用方法

    目录 String类基本概念 String字符串的存储原理 String类的常用构造方法 String类中常用方法 StringBuffer类 StringBuilder类 String类基本概念 String类属于引用数据类型,不属于基本数据类型. 在Java中只要是" "(双引号)中的,都是String对象. java中规定,双引号中的字符串是不可变的,也就是说"abc"自出生到死亡都不可能变成"abcd",也不能变成"ab&quo

  • C语言与C++内存管理超详细分析

    目录 一.内存 1.1 内存四区 1.2 使用代码证实内存四区的底层结构 二.malloc 和 free 2.1 malloc 和 free 的使用 2.2 内存泄漏与安全使用实例与讲解 三.new 和 delete 3.1 new 和 delete 使用 3.2 delete 与 delete[] 的区别 一.内存 在计算机中,每个应用程序之间的内存是相互独立的,通常情况下应用程序 A 并不能访问应用程序 B,当然一些特殊技巧可以访问,但此文并不详细进行说明.例如在计算机中,一个视频播放程序与

  • 详细分析JVM类加载机制

    目录 前言 1. jvm 的组成 2. 类加载 1. 加载 2. 链接 3. 初始化 3. 类加载器 引导类加载器(启动类加载器) 扩展类加载器 应用程序类加载器 4. 双亲委派机制 5. 类的主动/被动使用 结语 前言 ladies and gentleman , 你们好 ,我是羡羡 , 这节我们进入jvm的学习 , 我们知道 , jvm是java虚拟机, java代码的执行与 jvm 息息相关, 接下来我们来依次介绍 , 首先这节先来介绍 jvm 中的类加载部分 1. jvm 的组成 jvm

  • Spring Boot 详细分析Conditional自动化配置注解

    目录 1. Spring Boot Condition功能与作用 2. Conditional条件化系列注解介绍 3. Conditional条件化注解的实现原理 4. Conditional核心之matches匹配接口 5. Conditional核心之条件化注解具体实现 6. 总结 1. Spring Boot Condition功能与作用 @Conditional是基于条件的自动化配置注解, 由Spring 4框架推出的新特性. 在一个服务工程, 通常会存在多个配置环境, 比如常见的DEV

  • SpringBoot自动配置特点与原理详细分析

    目录 一.SpringBoot是什么 二.SpringBoot的特点(核心功能) 三.SpringBoot的自动配置原理 1. @SpringBootApplication 2. @SpringBootConfiguration 3. @EnableAutoConfiguration 4. @ComponentScan 四.核心原理图 五.常用的Conditional注解 一.SpringBoot是什么 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Sprin

随机推荐