从汇编语言角度剖析右值引用和左值引用的本质

汇编视角:右值引用与左值引用解析
本文从汇编语言的角度探讨右值引用和左值引用的本质,指出两者皆为编译器提供的功能,本质上是特殊的指针形式。左值引用类似常量指针,右值引用则通过匿名变量延长右值生命周期。

右值引用着实让人很挠头,而他又很重要,看多了“右值是短暂的,左值是持久的”这种正确但是让人摸不到头脑的话,我觉得很有必要从右值引用和左值引用实现的层面进行一次区分。

首先明确一点,左值引用和右值引用,都是指针。

其实说白了他们都是编译器给我们提供的“功能”,也就是说,编译器在我们看不到的地方做了很多别的事情,让一个指针的用法变得如此丰富多彩。但是不管多么丰富多彩,他也只是一个指针而已。

先来看左值引用。编译器对他做的操作是“自动提领”,就是自动加个*操作。

dword ptr[x] = *x

    int a;
    int&x=a;
009913DE  lea         eax,[a]                     lea是取地址指令,把a的地址放到eax寄存器里
009913E1  mov         dword ptr [x],eax    把eax寄存器里的值放到
    int*p=&a;
009913E4  lea         eax,[a]  
009913E7  mov         dword ptr [p],eax 

来看看汇编代码,编译器做了什么一览无遗。可以说,完 全 一 致。不管是x还是p,都只不过是一个指向a的指针罢了。

所以左值引用更像一个常量指针,int *const,它和常量指针唯一的区别就在于他不用每次都让我们自己写*了,除此以外真的没什么区别。

值得一说的还有常量引用直接由右值初始化的事情,右值是什么等会再说,总之下面的1就是一个右值。

    int a=1;
00EF13DE  mov         dword ptr [a],1  
    const int& x=1;
00EF13E5  mov         dword ptr [ebp-20h],1  
00EF13EC  lea         eax,[ebp-20h]  
00EF13EF  mov         dword ptr [x],eax  

在这里首先编译器把1赋值给了一个无名的变量,然后又把这个无名变量的地址给了x。再想想onst int&x就是const int* const x,变量的值不会改变,指针值的变量不会改变,等于说这个x就和1彻底的绑在了一起,中间的那个无名变量永远不会被外界接触到。

再来说说右值的概念,判断右值和左值的最好方法就是,看它能不能取地址。

    int a=1;
010913BE  mov         dword ptr [a],1  
    int b=1;
010913C5  mov         dword ptr [b],1  
    int c=a+b;
010913CC  mov         eax,dword ptr [a]  
010913CF  add         eax,dword ptr [b]  
010913D2  mov         dword ptr [c],eax  

在这里a+b就是一个右值,它是活在寄存器里的一个值,他在内存里根本没有存在的位置,你无法对它取地址,这就是个右值。

int f()
{
	int a=3;
	return a;
}
int main ()
{
	int x=f();
}

    int x=f();
009517DE  call        f (09511D6h)  
009517E3  mov         dword ptr [x],eax  

函数的返回值是存在寄存器里的,只要是活在寄存器里的变量,都是右值。所以回想一下计算机是怎么在汇编层面进行运算的,就很容易判断什么是右值,什么是左值。

现在来看看右值引用,这里先说结论右值引用就是一个指向一个匿名变量的指针。还记得const int&x吗,刚才说那个中间的匿名变量永远不会被接触,现在右值引用就是给了外界接触这个匿名变量的机会。

    int&& x=f();
00C729EE  call        f (0C711D6h)  
00C729F3  mov         dword ptr [ebp-14h],eax  
00C729F6  lea         eax,[ebp-14h]  
00C729F9  mov         dword ptr [x],eax  

可见初始化一个右值引用其实是开辟了两块空间,一块是右值引用类型那么大的匿名变量,一块是指向这个匿名变量的指针。初始化的时候先从一个返回右值的寄存器中把对应的右值赋值给匿名变量,再把匿名变量的地址取出来,赋值给x这个指针。

    x=3;
009829FC  mov         eax,dword ptr [x]  取x中存的地址,放到eax
009829FF  mov         dword ptr [eax],3  把3赋值给eax存的地址指向的内存空间

改变右值引用值的过程也分为两步,取出指针x的值,也就是匿名变量的地址,把右值赋值给地址所指的匿名变量。

看看汇编代码,和const int&何其相似,改变右值引用的过程就是改变匿名变量值的过程。现在想想在书本上看的那些话“右值引用延长了右值的生命周期”,直到我们看透了右值引用的本质才能理解这话到底是什么意思。右值被放到了一个变量里,那当然就是避免了被从寄存器里扔出去就消失的命运了,他已经是一个有内存空间的变量了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值