这是我10个月前看到的一篇博客吧,感觉分析指针和引用的文章这是我目前见过讲解得最清晰的一篇:

本文主要基于反汇编代码,从初始化、赋值以及取地址三个角度来理解指针和引用的区别。
初始化
写出以下代码并查看反汇编代码:

int main()
{int x = 5;int * ptr = &x;  //指针int & ref = x;   //引用return 0;
}


在初始化阶段,指针和引用的行为都是一样的:先将x的地址加载到寄存器eax中,然后把eax的值拷贝到另一个内存地址中。

从反汇编中可以看到,x的地址就是ebp-0ch,对于指针来说,用x的地址来初始化指针ptr实际上就是把x的地址ebp-0c放到了ptr的地址单元中,而ptr的地址则是ebp-18h;

对于引用来说,这里就有点问题了:常说的引用都是“变量的别名”,似乎ref和x就应该是同一个地址,而实际上这里做了和指针初始化相同的操作——把x的地址放到了另一个内存单元(地址为ebp-24h)中。其实在这就可以有一种猜测:虽然ref是x的引用,但是ref也有自己的地址的,而在初始化阶段,它的地址单元中存放的是它所引用的变量x的地址。如下所示:

为了便于叙述,下文中把ref叫做“引用变量”,来表明它也是一个有地址的“变量”(这种说法并不准确,只是暂时找不到其它说法)。
赋值
由于自加自减也是一个赋值的过程,为了便于叙述,这里就用自加来进行分析。写出以下代码并查看反汇编代码:

int main()
{......ptr++;   //指针自加ref++;   //引用自加return 0;
}


从反汇编代码可以看到,指针的自加和引用的自加是不同的,引用的步骤更多。

在指针方面,一共有三步:①把指针变量ptr内存单元中的数据拷贝到eax;②eax加4(这里的4对应32位编译器下int型的size);③将eax的值写回到指针变量ptr的内存单元中。这三个步骤其实就做了一件事:把指针变量ptr内存单元中的数据加4。而在此之前指针变量ptr的内存单元中存放的是变量x的地址,因此,这就相当于断开了ptr和x之间的联系;

在引用方面,一共有五步:①把引用变量ref内存单元中的数据拷贝到eax;②根据eax的值找到相应的内存单元并把该内存单元中的数据拷贝到ecx中;③ecx加1;④再次把引用变量ref内存单元中的数据拷贝到edx中;⑤把ecx的值写到edx的值对应的内存单元中。要分析这五步做了什么,一定要知道在此之前,引用变量ref的内存单元中存的是变量x的地址。这五步做的事就是:根据ref存放的x的地址来找到x的内存单元,然后把x的内存单元中的值加1。从这个过程可以知道,ref++;表面上是对ref进行自加,而实际上,ref只是一个媒介,通过ref找到x,然后对x进行自加。

通过对二者赋值,可以发现,引用变量ref的确有自己的地址,它在内存中是占空间的。并且指针变量ptr完全具有“主导权”,对指针变量ptr进行赋值,改变的就是它本身;而引用变量ref则完全没有“主导权”,对引用变量ref进行赋值,改变的并不是它本身,而是通过它所找到的x。如下所示:

取地址
对ptr和ref分别进行取地址,这里的变量t和t1可以不用管它,只需要观察取地址的过程。查看相应反汇编代码:

int main()
{......//取地址int ** t1 = &ptr;int * t = &ref;return 0;
}


由于不需要管t和t1,因此只需要关注ptr和ref取地址各自反汇编代码第一行即可。

在指针方面,取出ptr的地址&ptr是通过lea eax,[ebp-18h]来实现的。这句汇编代码的意思是,把ebp-18h这个地址,加载到eax中。而在ebp-18h就是指针变量ptr的内存地址,因此,指针变量ptr取地址很直接,就是取出ptr的内存地址;

再看引用方面,取出ref的地址&ref是通过mov eax,dword ptr [ebp-24h]实现的。这句汇编代码的意思是,取出ebp-24h这个地址的内存单元中的值,拷贝到eax中。根据前面已经知道,ebp-24h是ref的地址,在这个地址中存放的是ref引用的x的地址,因此&ref实际上是x的地址

由此可以发现,对指针取地址,取出的就是指针变量的地址,而对引用取地址,取出的实际上是引用变量所在内存单元中的值,也就是它引用的变量的地址

总结

1.引用和指针都是占用内存的。引用变量和指针变量各自的内存单元中,存放的都是它们引用或指向的变量的地址;

2.引用实际上是把引用变量本身给隐藏了,表面上对引用变量进行赋值,实际上改变的是它引用的变量的值;表面上是对引用变量取地址,实际上取出来的是它引用的变量的地址;

3.由于引用变量的操作的实际对象都是引用变量所在内存单元中的值,因此引用变量必须和另一个变量关联起来。也就是说,引用必须初始化。原因很简单,如果你不对一个引用进行初始化,那么引用变量的内存单元中存放的都是垃圾数据,后面对引用变量进行操作时就会通过这些垃圾数据找到对应的内存地址,这是非常危险的;

那么直接初始化的时候对引用赋一个常量值呢?这实际上也是不对的,因为引用的初始化实际是用用来初始化的变量的地址来初始化引用变量它自己的内存单元,而一个常量值哪有地址呢?虽然在C++11中已经可以用一个常量来初始化右值引用,但是通过反汇编可以看到,右值引用的根本,还是先用一个地址去保存这个常量值,然后再用这个地址去初始化引用变量。

而指针变量则完全不一样了,指针变量的操作的实际对象都是它自己,因此指针变量不像引用那样必须初始化,但是为了安全,也应该初始化。

4.依然是由于引用变量的一切操作实际对象都是它引用的变量,因此从用户角度来说是没有入口让用户去修改引用变量本身的。换句话说,用户层面没有任何办法去改变引用变量内存单元中存放的地址,这也就是为什么引用一旦初始化后就无法再修改。

5.由于引用变量自身对于用户是不可见的,对引用变量取地址得到的也不是引用变量的地址,因此你无法让一个引用变量的内存中存放另一个引用变量的地址,换句话说,不存在引用的引用。而相反,由于指针变量取出来的地址就是它本身的地址,因此你完全可以把一个指针变量的地址存放在另一个指针变量的内存中,这也就是为什么可以存在多级指针,但是多级引用是不允许的。

6.关于“引用是变量的别名”的考虑。感觉这句话既对也不对,说它不对是因为引用变量和引用的变量二者实际上是独立的两块内存单元,只不过前者依托后者而存在,但是这并不能说前者就是后者的别名,这是矛盾的;说它对,是因为对引用变量进行操作时,改变的对象实际上都是引用的变量而不是引用变量,这就感觉引用变量只是一层伪装,真正的还是它引用的变量,从这个角度来说,引用确实可以说是变量的别名。

7.函数传指针与传引用的区别。函数传指针的原型类似于int add(int * a, int * b);这类函数的形参是指针,调用方式为int x = 1, y = 2; add(&x,&y);那么传入的实参则是变量的地址,因此在函数内部,是用地址型实参&x和&y来初始化形参a和b,相当于int *a = &x,int * b = &y的;因此形参a和b内存单元中存放的是x和y的地址。

而对于函数传引用,函数原型则类似于int add(int & a,int & b),这类函数的形参是引用,调用方式为int x = 1, y = 2; add(x,y);传入的实参就是x和y变量自身,在函数内部,则是用x和y来初始化a和b,相当于int &a = x,int &b = y,因此,形参a和b内存单元中存放的也是x和y的地址,不过它毕竟是引用,如前面所说,当你试图对形参a或b的值进行改变的时候,改变的不是它内存中存放的&x和&y,其实是x或y;当你对形参a或b取地址时,取出来的并不是形参本身的地址,而是它内存中存放的&x和&y。

而再说一个与本文无关的函数传值,如int add(int a,int b);这类函数的形参只是普通的int型变量,当调用add(x,y)时,在函数内部实际上就是用x和y来初始化a和b,相当于int a = x,int b = y,所以形参和实参实际上只是值相同而已,改变形参并不会影响实参。

2020-12-6(从反汇编理解指针和引用的区别)相关推荐

  1. 3分钟理解指针和引用的区别【❤️建议收藏❤️】

  2. c++值传递,指针传递,引用传递以及指针与引用的区别

    值传递: 形参是实参的拷贝,改变形参的值并不会影响外部实参的值.从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入, 不能传出.当函数内部需要修改参数,并且不希望这个改变影 ...

  3. 【转】指针和引用的区别

    c++中的引用与指针的区别 ★ 相同点: 1. 都是地址的概念: 指针指向一块内存,它的内容是所指内存的地址:引用是某块内存的别名. ★ 区别: 1. 指针是一个实体,而引用仅是个别名: 2. 引用使 ...

  4. C++中指针和引用的区别与联系

    原文地址:http://blog.csdn.net/thisispan/article/details/7456169 ★ 相同点: 1. 都是地址的概念: 指针指向一块内存,它的内容是所指内存的地址 ...

  5. C语言_指针和引用的区别

    文章目录 指针和引用的区别 0.前言 1.指针.指针变量和引用 2.指针变量和引用变量的定义 3.引用的用途 4.引用和指针的区别 5.引用的底层原理 6.代码示例 6.1.示例程序1 6.2.示例程 ...

  6. 24.指针和引用的区别

    在面试时经常会被问到 指针和引用的区别,虽然问题很简单但还是总结一下! 指针和引用的区别其实有太多了,主要的区别大概是这样子: 1)指针声明的时候可以不被初始化(有可能成为野指针),但是引用必须要进行 ...

  7. C/C++ 中指针和引用的区别

    C/C++ 中指针和引用的区别 1.指针有自己的一块空间,而引用只是一个别名: 2.使用 sizeof 看一个指针的大小是 4,而引用则是被引用对象的大小: 3.指针可以被初始化为 NULL,而引用必 ...

  8. python指针引用的区别_C++基础:指针和引用的区别

    C++基础:指针和引用的区别 *例 int a; int &b = a; 其中b是a的引用,b引用了a,a被b引用.b 相当于 a 的别名,对 b 的任何操作就是对a的操作.所以b既不是a的拷 ...

  9. C++中指针与引用的区别

    指针的本质 指针p也是对象,只不过p存储的数据类型是它所指的对象的地址.可以通过解引用操作符""来访问对象的值,即p. 对象有常量(const)和非常量之分,如果指针本身是常量,即 ...

最新文章

  1. 变了味的微信你还能用多久?
  2. AI现在能教你画画了
  3. 解决远程连接超过最大连接数问题
  4. OleDb执行Oracle带自定义函数的SQL深度历险
  5. 【Python Flask】SQLAlchemy增删改查总结;不重复查询某一列
  6. (论文阅读笔记1)Collaborative Metric Learning(二)(WWW2017)
  7. 【OpenCV】OpenCV函数精讲之 -- imshow()函数
  8. python lambda 判断_在Python的Filter中使用lambda函数时,为何达不到预期效果?
  9. 化妆品包装新趋势|视觉模型样机包装模板,让你茅塞顿开
  10. how tomcat works 读书笔记(一)----------一个简单的webserver
  11. 二级公共基础之——数据结构与算法
  12. SFB 项目经验-35-分配公网证书 For Exchange Server 2016(图解)
  13. 饿了么app的架构演进之路,你的外卖可不简单
  14. 前端利用jQuery设置日期选择框
  15. Coremail-0day敏感文件泄露漏洞送附批量检测脚本
  16. 面试相关(技术汇总)
  17. 黄教头第六周作业 一个基础的反射型xss
  18. bwt比对算法 C语言,BWT比对算法
  19. AUTOSAR-Fee模块
  20. Latex——属于符号

热门文章

  1. 京瓷2010复印a4内容不全_京瓷2010复印机,纸卡定影的故障
  2. 成功解决raise Py4JError py4j.protocol.Py4JError: py4j.protocol.Py4JError: Could not find py4j jar at
  3. Matlab:成功解决Function definition are not permitted at the prompt or scripts
  4. PyQt之Eric:成功解决No module named 'my_image_rc'
  5. MAT之DT:DT实现根据乳腺肿瘤特征向量高精度预测肿瘤的是恶性还是良性
  6. 【十问十答】粒子群算法(PSO)
  7. 南阳oj 1的个数
  8. JavaScript 表单编程
  9. JS 版的pnp in_array($str,$arr)
  10. 如何让div水平垂直居中