——by karottc
分析一下 C++ 裡面的引用類型(例如: int &r = a; )中的 r
變量是否占用內存空間呢?是否和 int *p = &a; 中的 p
變量一樣占用內存空間呢?
本文將給出答案。
直接看一個簡單的例子:
#include <iostream> using namespace std; int main(void) { int a = 6; int &r = a; int *p = &a; int x = r; return 0; }
接著我們通過 g++ testref.cpp -o testref -g 來編譯之後,用gdb來加載它,看看 a
, r
, p
, x
的地址分別是多少:
上圖中 a
的地址為 0x7fffffffe208
,用取地址符去獲取 r
地址,值和 a
是一樣的,其實,用 &
符號是不能獲取到引用類型變量的地址的,因為引用類型的變量本身只是另一個對象的別名,用可感知的方式描述,就是它僅僅是一個名字而已,對它的任何操作都是相當於對另一個對象的操作,所以這個取地址操作也是一樣。
但是,我們接著往下看,p
的地址是 0x7fffffffe218
,和 a
的地址剛剛相差了 16 字節,接著看 x
的地址是0x7fffffffe20c
,這個地址正好是 a
的地址 0x7fffffffe208 + 4
, 而 a
是 int
型,變量本身占用 4 字節,正常情況下, x
的地址應該是 p
的地址 0x7fffffffe218 + 8
,這裡 +8
是因為我的機器是 64 位的機器,所以指針類型占用 8 字節。現在這種情況應該是編譯器做了優化,把 x
的放到了 a
的後面,同樣的,x
的下一個變量的起始地址應該是 0x7fffffffe20c + 4
=0x7fffffffe210
,這個地址和 p
之間剛好差了 8 個字節,也就是一個指針變量的地址,所以答案就很明顯了。
即,我們現在可以猜測,r
是占用內存空間,並且占用的大小和指針變量相同大小。但是我們上面用gdb也看不了 r
的實際地址,所以這也只能是猜測,只是看似合理的猜測而已。
為了證明這個猜測,我們繼續把可執行文件反匯編出來看看:objdump -d testref
得到的結果如下:
00000000004006cd <main>: 4006cd: 55 push %rbp 4006ce: 48 89 e5 mov %rsp,%rbp 4006d1: c7 45 e8 06 00 00 00 movl $0x6,-0x18(%rbp) # int a = 6; a的地址 0x18 4006d8: 48 8d 45 e8 lea -0x18(%rbp),%rax 4006dc: 48 89 45 f0 mov %rax,-0x10(%rbp) # int &r = a; r的地址 0x10 4006e0: 48 8d 45 e8 lea -0x18(%rbp),%rax 4006e4: 48 89 45 f8 mov %rax,-0x8(%rbp) # int *p = &a; p的地址 0x8 4006e8: 48 8b 45 f0 mov -0x10(%rbp),%rax # 下面兩行是為了後面的賦值做准備 4006ec: 8b 00 mov (%rax),%eax 4006ee: 89 45 ec mov %eax,-0x14(%rbp) # int x = r; x的地址 0x14 4006f1: b8 00 00 00 00 mov $0x0,%eax 4006f6: 5d pop %rbp 4006f7: c3 retq
我把對應處,都寫在上面的注釋裡了。
所以,現在可以得到結論:引用類型的變量會占用內存空間,占用的內存空間的大小和指針類型的大小是相同的。 從上面的匯編代碼可以看出,雖然引用是一個對象的別名,但是在匯編層面,和指針是一樣的。
【本文首發於:http://www.karottc.com/blog/2015/07/29/cpp-reference/】
2015.07.29