目錄:
初學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的地址,從而可以知道,從地層實現來看,引用變量的確存放的是被引用對象的地址,只不過,對於高級程序員來說是透明的,編譯器 屏蔽了引用和指針的差別。
如果還不明白,我們繼續看這段代碼變量的內存佈局圖:
最後要注意一點的是:引用就是引用,指針就是指針,引用不代表指針,一定不要混淆。