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的地址,從而可以知道,從地層實現來看,引用變量的確存放的是被引用對象的地址,只不過,對於高級程序員來說是透明的,編譯器 屏蔽了引用和指針的差別。

如果還不明白,我們繼續看這段代碼變量的內存佈局圖:
這裏寫圖片描述

最後要注意一點的是:引用就是引用,指針就是指針,引用不代表指針,一定不要混淆。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章