c++中“引用”的底层实现原理详解

目录:

初学c++中的“引用”这一概念的时候,很多人都是懵的,大家大概都会产生这样的疑问?
什么是引用?
引用占用内存吗?
……

于是,为了验证你的猜想,你可能会写出下面这样的代码来验证:

#include<iostream>
using namespace std;
int main()
{
    int  a = 1;
    int&  b = a;
    cout << "a:address->" << &a << endl;
    cout << "b:address->" << &b << endl;

    getchar();
    return 0;
}

运行结果:
a:address->0031FD54
b:address->0031FD54

我们会发现,引用b的地址和变量a的地址一样。于是,有人猜想是不是说变量a和引用b本身就是一个东西。所以同样的,引用本身所占内存就是变量a的内存。

首先对于这个说法,肯定是不正确的。至于为什么不正确,我们接下来会以底层原理为大家解释。

什么是引用

为了看看引用的底层究竟是怎样实现的,我决定写一个简短的代码,然后反汇编(vs编译环境在调试模式下,右键鼠标菜单->反汇编)看一看。

#include<iostream>
using namespace std;
int main()
{
int x=1;
int &b=x;
return 0;
}

我们再看下转汇编后的汇编代码:

9:       int x = 1;     //源代码 
00401048   mov         dword ptr [ebp-4],1  //反汇编代码  
10:      int &b = x;    //源代码
0040104F   lea         eax,[ebp-4]          //反汇编代码
00401052   mov         dword ptr [ebp-8],eax//反汇编代码

在这里解释下这三行反汇编代码:
mov dword ptr [ebp-4],1 //把1赋值给ebp(栈底指针)-4的地址
lea eax,[ebp-4] //把ebp-4的地址赋值给寄存器eax
mov dword ptr [ebp-8],eax //把寄存器eax里的值赋值给ebp-8的这块地址
上述三行代码的作用就是将1赋值给x,然后将x的地址赋值给了引用b。
而在内存中,它是这样的:
这里写图片描述
注意:因为栈在内存中是由高地址向低地址增长的
通过底层的分析,我们不难理解引用的本质就是所引用对象的地址

建议:有兴趣的同学可以了解一下常见的汇编指令,对于了解代码底层原理有很大的帮助。

引用占用内存吗

通过上面的分析,我们得出了引用本身存放的是引用对象的地址,通俗点理解就是引用就是通过指针来实现的,所以,应用所占的内存大小就是指针的大小。

引用的地址

在最开始,我们写过一段代码来测试引用的地址,发现引用的地址和变量的地址是一样的。但是,在后面对引用的底层分析后发现,它本身又存放的是变量的地址,即引用的值是地址,那么这不是很冲突吗?

事实上, b的地址我们没法通过&b获得,因为编译器会将&b解释为:&(*b) =&x ,所以&b将得到&x。也验证了对所有的b的操作,和对x的操作等同。

那么问题来了,我们如何才能获得引用的地址呢?
我们看下面这段代码:

#include<stdio.h>
#include <iostream>  
using namespace std;
int main()  
{  
   int x = 1;  
   int y = 2;  
   int &b = x;  
   printf("&x=%x,&y=%x,&b=%x,b=%x\n",&x,&y,&y-1,*(&y-1));  
   return 0;
 }   

输出:
&x=12ff7c,&y=12ff78,&b=12ff74,b=12ff7c

不知道看到这里大家明白了没有,引用b的地址我们可以间接通过&y-1来得到b的地址,从而得到b的值:*(&y-1) 从结果可以知道,b的值即x的地址,从而可以知道,从地层实现来看,引用变量的确存放的是被引用对象的地址,只不过,对于高级程序员来说是透明的,编译器 屏蔽了引用和指针的差别。

如果还不明白,我们继续看这段代码变量的内存布局图:
这里写图片描述

最后要注意一点的是:引用就是引用,指针就是指针,引用不代表指针,一定不要混淆。

猜你喜欢

转载自blog.csdn.net/lws123253/article/details/80353197