C++引用和指针的区别

article/2025/9/20 19:43:51

作者:RainMan
链接:https://www.zhihu.com/question/37608201/answer/545635054
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

引用是C++引入的重要机制(C语言没有引用),它使原来在C中必须用指针来实现的功能有了另一种实现的选择,在书写形式上更为简洁。那么引用的本质是什么,它与指针又有什么关系呢?

1.引用的底层实现方式

引用被称为变量的别名,它不能脱离被引用对象独立存在,这是在高级语言层面的概念和理解,并未解释引用的实现方式。常见错误说法是“引用”自身不是一个变量,甚至编译器可以不以引用分配空间。

实际上,引用本身是一个变量,只不过这个变量的定义和使用与普通变量有显著的不同。为了解引用变量底层实现机制,考查如下代码:

int i=5;
int &ri=i;
ri=8;

在Visual Studio 2017环境的debug模式调试代码,反汇编查看源码对应的汇编代码的步骤是:调试->窗口->反汇编,即可得到如下原码对应的汇编代码:

int i=5;
00A013DE  mov        dword ptr [i],5    	//将文字常量5送入变量i
int &ri=i;
00A013E5  lea        eax,[i]  	 	    	//将变量i的地址送入寄存器eax
00A013E8  mov        dword ptr [ri],eax  	//将寄存器的内容(也就是变量i的地址)送入变量ri
ri=8;
00A013EB  mov        eax,dword ptr [ri]  	//将变量ri的值送入寄存器eax
00A013EE  mov        dword ptr [eax],8   	//将数值8送入以eax的内容为地址的单元中
return 0;
00A013F4  xor        eax,eax

考查以上代码,在汇编代码中,ri的数据类型为dword,也就是说,ri要在内存中占据4个字节的位置。所以,ri的确是一个变量,它存放的是被引用对象的地址。由于通常情况下,地址是由指针变量存放的,那么,指针变量和引用变量有什么区别呢?使用指针常量实现上面的代码功能。考查如下代码:

int i=5;
int* const pi=&i;
*pi=8;

按照相同的方式,在VS2017中得都如下汇编代码:

int i=5;
011F13DE  mov         dword ptr [i],5  
int * const pi=&i;
011F13E5  lea         eax,[i]  
011F13E8  mov         dword ptr [pi],eax  
*pi=8;
011F13EB  mov         eax,dword ptr [pi]  
011F13EE  mov         dword ptr [eax],8  

观察以上代码可以看出:

(1)除了pi与ri变量名不同,所得汇编代码与第一段所对应的汇编代码完全一样。所以,引用变量在功能上等于一个指针常量,即一旦指向某一个单元就不能在指向别处。

(2)在底层,引用变量由指针按照指针常量的方式实现。

2.高级语言层面引用与指针常量的关系

1.引用和指针,在内存中都是占用4个字节(32bits系统中)的存储空间。指针和引用存放的都是被引用对象的地址,都必须在定义的同时进行初始化。

2.指针常量本身(以p为例)允许寻址,即&p返回指针常量(常变量)本身的地址,被引用对象用*p表示;引用变量本身(以r为例)不允许寻址,&r返回的是被引用对象的地址,而不是变量r的地址(r的地址由编译器掌握,程序员无法直接对它进行存取),被引用对象直接用r表示。

3.凡是使用了引用变量的代码,都可以转换成使用指针常量的对应形式的代码,只不过书写形式上要繁琐一些。反过来,由于对引用变量使用方式上的限制,使用指针常量能够实现的功能,却不一定能够用引用来实现。

例如,下面的代码是合法的:

int i=5,j=6;
int* const array[]={&i,&j};

而如下代码是非法的:

int i=5,j=6;
int& array[]={i,j};

也就是说,数组元素允许是指针常量,却不允许是引用。C++语言机制如此规定,原因是避免C++语法变得过于晦涩。假如定义一个“引用的数组”,那么array[0]=8;这条语句该如何理解?是将数组元素array[0]本身的值变成8呢,还是将array[0]所引用的对象的值变成8呢?对于程序员来说,这种解释上的二义性对正确编程是一种严重的威胁,毕竟程序员在编写程序的时候,不可能每次使用数组时都要回过头去检查数组的原始定义。

即得出两个不同:引用只能在定义时被初始化一次,之后不可变,但是指针可变;引用没有 const,指针有 const。

4.一些其他不同:

引用使用时无需解引用(*),指针需要解引用;

“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

引用不能为空,指针可以为空;

指针和引用的自增(++)运算意义不一样;引用自增被引用对象的值,指针自增内存地址。

 

https://blog.csdn.net/tianguiyuyu/article/details/102941951

c++中引用的本质

c++中引用就是一个常指针,其也占用内存空间。

1)引用在C++中的内部实现是一个常指针

Type& name 《-》Type* const name

2)C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。

3)从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏

具体点:如下:

1)引用在实现上,只不过是把:间接赋值成立的三个条件的后两步和二为一

(简介赋值的三个条件: 1定义两个变量 (一个实参一个形参) 2建立关联 实参取地址传给形参  3 *p形参去间接的修改实参的值)

//当实参传给形参引用的时候,只不过是c++编译器帮我们程序员手工取了一个实参地址,传给了形参引用(常量指针)

如下图所示,c++编译器会把左边的代码翻译成右边的。

2)当我们使用引用语法的时,我们不去关心编译器引用是怎么做的

    当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的。

详细点就是看下面的代码:

#include <iostream>using namespace std;//1 第一点 单独定义的引用时,必须初始化;说明很像一个常量void main1(){//const int c1 = 10;int a = 10;int& b = a; //b很想一个常量printf("&a:%d \n", &a);printf("&b:%d \n", &b); //===> a 和 b就是同一块内存空间的门牌号,a和b的地址是一样的,说明引用b就是a的别名cout << "hello..." << endl;system("pause");return;}//2 普通引用有自己的空间吗? 有struct Teacher{char name[64]; //64int age; //4int& a; //4 0 //很像指针 所占的内存空间大小int& b; //4 0};//3 引用的本质void modifyA(int& a1){a1 = 100;}void modifyA2(int* const a1){*a1 = 200; //*实参的地址 ,去间接的修改实参的值}void main(){int a = 10;//1modifyA(a); //指向这个函数调用的时候,我们程序员不需要取a的地址printf("a:%d \n", a); //输出100//a = 10;modifyA2(&a); //如果是指针 需要我们程序员手工的取实参的地址printf("a:%d \n", a); //输出200printf("sizeof(Teacher):%d \n", sizeof(Teacher)); //输出76,说明普通引用有自己的内存空间system("pause");}void modifyA3(int* p){*p = 200; //*p 3*p形参去间接修改实参的值}//间接赋值void main11(){int a = 10;int* p = NULL; //间接赋值成立的三个条件 1 定义两个变量p = &a;*p = 100;{*p = 200;}modifyA3(&a); //2 建立关联}// 123 写在一块// 12 3//1 23

 


http://chatgpt.dhexx.cn/article/AcS0bgKS.shtml

相关文章

引用和指针的区别

引用和指针的区别: C的引用(Reference) 1.定义引用就是给某个变量起别名&#xff0c;对引用的操作和对该变量操作完全相同。 int a 10&#xff1b; int& b a;//b就是a的别名 b; cout << a << endl;//11 2 常引用 1&#xff09;定义引用时加const修饰&#…

C++中引用和指针的区别

下面用通俗易懂的话来概述一下&#xff1a; 指针-对于一个类型T&#xff0c;T*就是指向T的指针类型&#xff0c;也即一个T*类型的变量能够保存一个T对象的地址&#xff0c;而类型T是可以加一些限定词的&#xff0c;如const、volatile等等。见下图&#xff0c;所示指针的含义&am…

C/C++引用和指针的区别

为什么C/C语言使用指针&#xff1f; 答案&#xff1a;①一方面&#xff0c;每一种编程语言都使用指针。不止C/C使用指针。 每一种编程语言都使用指针。C将指针暴露给了用户(程序员)&#xff0c;而Java和C#等语言则将指针隐藏起来了。 “Everything uses pointers. C just expo…

C++ 引用详解(引用的特点,引用与指针的区别,引用的其他使用)

目录 引用一.引入</font>二.C中较为麻烦的运算符</font>三.引用的定义</font>四.引用的特点五.对比指针与引用六.引用与指针的区别&#xff08;重点&#xff09;1.语法层面的区别2.汇编层面的区别 七.引用的其他使用 引用 一.引入 在生活中&#xff0c;我们…

Zipkin和Sleuth

“sleuth的作用是在系统中自动埋点并把数据发送给zipkin,zipkin的作用是存储这些数据并展现。” 说白了 sleuth就是为了Zipkin服务的 看了一圈 总结一下 就是微服务的链路追踪 我们看这个延迟的时间可以判断哪个服务出现了问题 进而排查出问题 简单说&#xff1a;假如服务 …

spring-cloud-sleuth分布服务跟踪式

why: 1,微服务架构微服务增多&#xff0c;一个客户端请求形成一个复杂的分布式服务调用链路&#xff0c;如果任何一个服务延迟过高或错误&#xff0c;都会引起请求失败。 how&#xff1a; 先引入了example&#xff1a; https://github.com/spring-cloud/spring-cloud-sleuth 1…

笔记:Sleuth 的底层逻辑

Sleuth 是通过打入到 Log 中的“卧底”来串联前后日志的。你集成了 Sleuth 组件之后&#xff0c; 它会向你的日志中打入三个“特殊标记”&#xff0c;其中一个标记你应该已经清楚了&#xff0c;那便是 Trace ID。剩下的两个标记分别是 Span ID 和 Parent Span ID&#xff0c;…

sleuth介绍

spring Cloud Sleuth为 spring Cloud提供了分布式跟踪的解决方案&#xff0c;它大量借用了Google Dapper、 Twitter Zipkin和 Apache HTrace的设计&#xff0c;先来了解一下 Sleuth的术语&#xff0c; Sleuth借用了 Dapper的术语。 span&#xff08;跨度&#xff09;&#xff…

sleuth 链路追踪

一&#xff1a;什么是链路追踪 对于以前的单服务器项目而言&#xff0c;如果一个地方出了问题&#xff0c;很容易去找到。可是对于微服务架构项目来说&#xff0c;因为微服务太多&#xff0c;而且往往是一个微服务调用了很多其他的微服务&#xff0c;如果一个地方出错&#xff…

java sleuth配置

java sleuth配置 springCloud学习记录SpringCloud Alibaba sleuth&#xff08;分布式请求链路跟踪&#xff09;zipkinsleuthpomyml springCloud学习记录 SpringCloud Alibaba sleuth&#xff08;分布式请求链路跟踪&#xff09; 在微服务框架种&#xff0c;一个由客户端发起的…

Spring Cloud Sleuth+Zipkin 构建微服务链路跟踪系统

什么是链路跟踪系统&#xff1f; 在微服务中&#xff0c;多个服务分布在不同物理机器上&#xff0c;各个服务之间相互调用。如何清晰地记录服务调用过程&#xff0c;并在出现问题的时候能够通过查看日志和服务之间的调用关系来定位问题&#xff0c;这样的系统就叫做链路跟踪系…

SpringCloud Sleuth入门介绍

案例代码:https://github.com/q279583842q/springcloud-e-book 一、Sleuth介绍 为什么要使用微服务跟踪?它解决了什么问题&#xff1f; 1.微服务的现状&#xff1f; 微服务的现状   随着业务的发展&#xff0c;单体架构变为微服务架构&#xff0c;并且系统规模也变得越来…

Spring Cloud Sleuth HTTP详解

目录 1版本关系 2简介 2.1术语 2.2调用链可视化 2.2.1使用Zipkin进行分布式跟踪 2.2.2错误可视化 2.2.3使用Brave进行分布式跟踪 2.2.4将Sleuth添加到项目中 3调用信息生成与上报原理 3.1初始化配置类 3.2过滤器-请求过滤 3.3调用信息拦截 3.4调用信息上报 4日志…

Sleuth链路追踪

文章目录 Spring-Cloud-Alibaba-Sleuth链路追踪一、链路追踪介绍二、Sleuth入门演示&#xff1a; 三、ZipKin介绍ZipKin服务端安装ZipKin数据持久化 Spring-Cloud-Alibaba-Sleuth链路追踪 一、链路追踪介绍 在大型的微服务项目中&#xff0c;一个系统被拆分成许多模块&#xf…

sleuth原理详解

sleuth原理 1、关键术语 Span&#xff1a;Span基本工作单元&#xff0c;发送一个远程调度任务就会产生一个Span, Span 是用 64ID唯一标识的&#xff0c;Trace是用另一个64ID唯一标识的。Span还包含了其他的信息&#xff0c;例如摘要、时间戳事件、Span的ID以及进程 ID。Trace…

07-搭建微服务-链路追踪Sleuth

1、为什么使用链路追踪&#xff1f; 在微服务中&#xff0c;随着服务越来越多&#xff0c;对调用链的分析越来越复杂。 出现问题&#xff1a; 1、微服务之间的调用错综复杂&#xff0c;用户发送的请求经历哪些服务&#xff0c;调用链不清楚&#xff0c;没有一个自动化的工具类来…

sleuth入门

Sleuth介绍 SpringCloud Sleuth主要功能就是在分布式系统中提供追踪解决方案。它大量借用了Google Dapper的设计&#xff0c; 先来了解一下Sleuth中的术语和相关概念。 Trace 由一组Trace Id&#xff08;贯穿整个链路&#xff09;相同的Span串联形成一个树状结构。为了实现请求…

集成sleuth_Spring Cloud Sleuth整合zipkin过程解析

这篇文章主要介绍了Spring Cloud Sleuth整合zipkin过程解析,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 SpringCloud Sleuth 简介 Spring Cloud Sleuth为Spring Cloud实现了分布式跟踪解决方案。 Spring Clo…

集成sleuth_整合Sleuth

Sleuth是 springcloud 分布式跟踪解决方案。 Sleuth 术语&#xff1a; 跨度(span ) &#xff1a;Sleuth 的基本工作单元&#xff0c;他用一个64位的id唯一标识。出ID外&#xff0c;span还包含 其他的数据&#xff0c;如 描述&#xff0c;时间戳事件&#xff0c;键值对注解等&am…

Spring Boot 中的 Sleuth 是什么, 如何使用

Spring Boot 是一个非常流行的 Java Web 开发框架&#xff0c;它提供了许多方便的功能&#xff0c;其中之一就是 Sleuth。Sleuth 是一个分布式跟踪系统&#xff0c;用于跟踪应用程序中的请求和操作。在本文中&#xff0c;我们将探讨 Spring Boot 中的 Sleuth 是什么&#xff0c…