C++ 引用的本质

Posted CPP开发者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 引用的本质相关的知识,希望对你有一定的参考价值。

(给CPP开发者加星标,提升C/C++技能)

来源:恋猫大鲤鱼
https://blog.csdn.net/K346K346/article/details/46805159

代码运行环境:

Windows7 32bits+VS2017+Win32。

引用是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
int i=5;
intconst 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.高级语言层面引用与指针常量的关系

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

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

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

而如下代码是非法的:

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

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

3.非正常使引用变量指向别的对象

C++语言规定,引用变量在定义的时候就必须初始化,也即是将引用变量与被引用对象进行绑定。而这种引用关系一旦确定就不允许改变,直到引用变量结束其生命期。这种规定是在高级语言的层面上,由C++语言和编译器所做的检查来保障实施的。在特定的环境下,利用特殊的手段,还是可以在运行时动态地改变一个引用变量与被引用对象的对应关系,使引用变量指向一个别的对象。见下面的程序:

#include <iostream>
using namespace std;

int main(int argc,char* argv[]) {
 int i=5,j=6;
 int &r=i;
 void *pi,*pj;
 int* addr;
 int dis;

 pi=&i;    //取整型变量i的地址
 pj=&j;    //取整型变量j的地址
 dis=(int)pj-(int)pi;//计算连续两个整型变量的内存地址之间距离
 addr=(int*)((int)pj+dis);//计算引用变量r在内存中的地址

 cout<<"&i:"<<pi<<endl;
 cout<<"&j:"<<pj<<endl;
 cout<<"&pi:"<<&pi<<endl;
 cout<<"&pj:"<<&pj<<endl;
 cout<<"&addr:"<<&addr<<endl;
 cout<<"&dis:"<<&dis<<endl;
 cout<<"distance:"<<dis<<endl;
 
 (*addr)=(int)&j;    //将j的地址赋给引用r(此处把r看作指针)
 
 cout<<"addr:"<<addr<<endl;
 r=100;
 cout<<i<<" "<<j<<endl;
 return 0;
}

这个程序在Debug模式下输出结果如下:

&i:0038FC1C
&j:0038FC10
&pi:0038FBF8
&pj:0038FBEC
&addr:0038FBE0
&dis:0038FBD4
distance:-12
addr:0038FC04
5 100


- EOF -


推荐阅读   点击标题可跳转

1、

2、

3、


关注『CPP开发者』

看精选C++技术文章 . 加C++开发者专属圈子

↓↓↓


点赞和在看就是最大的支持❤️

以上是关于C++ 引用的本质的主要内容,如果未能解决你的问题,请参考以下文章

引用向量的部分片段?

C++ 引用本质的理解①

值传递,指针传递;引用传递(c++独有)本质

C++中引用和匿名对象的理解和本质剖析

C++引用在本质上是什么,它和指针到底有什么区别?

Java的引用c++的引用和C指针的区别