C 引用占用記憶體?

2021-09-26 07:27:02 字數 3651 閱讀 8105

說到引用,一般c++的教材中都是這麼定義的:

1,引用就是乙個物件的別名。

2,引用不是值不佔記憶體空間。

3,引用必須在定義時賦值,將變數與引用繫結。

那你有沒有想過,上面的定義正確嗎?編譯器是如何解釋引用的?

這裡先給出引用的本質定義,後面我們再進一步論證。

1,引用實際是通過指標實現的。

2,引用是乙個常量指標。

3,引用在記憶體中佔4個位元組。

4,在對引用定義時,需要對這個常量指標初始化。

我們從最簡單的變數的定義開始,看編譯器會做哪些事情。

int var = 42;

mov dword ptr [var],2ah // 對應彙編**

上面語句申請了一塊記憶體空間,佔4個位元組,存放了乙個int型的變數。記憶體裡放的是42的二進位製碼。

那麼var這個變數名放在哪呢?

我們知道程式如果訪問記憶體裡的資料,需要通過位址來進行訪問,所以上面的**在經過編譯器生成目標**時,用存放42的位址了所有的var,所以結論時,目標檔案中不存在var,所以變數名本身是不佔記憶體的。

而我們知道,引用是變數的乙個別名。那麼,從這很多人會聯想到,引用會不會也只是乙個名字而已,編譯器在生成目標**的時候,會用實際位址替換引用呢?

答案並非這樣!

那我們接下來看看,當我們定義乙個引用時,發生了什麼:

1     int var = 42; 

2 01303ac8 mov dword ptr [var],2ah

3 int& refvar = var;

4 01303acf lea eax,[var]

5 01303ad2 mov dword ptr [refvar],eax

上面的**顯示,當定義乙個引用時,編譯器將var的位址賦給了以refvar為位址的一塊記憶體區域。也就是說refvar其實存放的是var的位址。

這讓我們聯想到了指標,那麼我們看看定義乙個指標是發生了什麼:

1     int var = 42; 

2 01213ac8 mov dword ptr [var],2ah

3 int* ptrvar = &var;

4 01213acf lea eax,[var]

5 01213ad2 mov dword ptr [ptrvar],eax

沒錯,沒有任何差別,定義乙個引用和乙個指標的彙編**完全一致!

相信從上面的分析時,你可能已經相信了,引用實際上就是乙個指標。那麼為什麼說引用是乙個常量指標呢,在目標**裡有什麼體現呢?

這個問題其實要從c++底層機制談起,c++為我們提供的各種訪問控制僅僅是在編譯階段給我們的限制,也就是說編譯器確保了你在完成任務之前的正確行為,如果你的行為不正確,那麼編譯器就是給你在編譯時提示錯誤。所謂的const和private等在實際的目標**裡根本不存在,所以在程式執行期間只要你願意,你可以通過記憶體工具修改它的任何乙個變數的值。

這也就解釋了為什麼上面的兩段**中引用和指標的彙編**完全一致。

c++設計引用,並用常量指標來從編譯器的角度實現它,目標是為了提供比指標更高的安全性,因為常量指標一旦與變數位址繫結將不能更改,這樣降低了指標的危險係數,它提供了一種一對一的指標。

但是你覺得使用引用就安全了嗎?它同樣會有與使用指標一樣的問題

1 int *var = new int(42); 

2 int &ref = *var;

3 delete var;

4 ref = 42;

5 return 0;

上面這段**就很不安全,因為ref引用的記憶體區域不合法。

為了進一步驗證引用與指標在本質上的相同,我們看當引用作為函式引數傳遞時,編譯器的行為:

1 void swap(int& v1, int& v2); 

2 void swap(int* v1, int* v2);

3 4 int var1 = 1;

5 00a64af8 mov dword ptr [var1],1

6 int var2 = 2;

7 00a64aff mov dword ptr [var2],2

8 swap(var1,var2);

9 00a64b06 lea eax,[var2]

10 00a64b09 push eax

11 00a64b0a lea ecx,[var1]

12 00a64b0d push ecx

13 00a64b0e call swap (0a6141fh)

14 00a64b13 add esp,8

15 swap(&var1, &var2);

16 00a64b16 lea eax,[var2]

17 00a64b19 push eax

18 00a64b1a lea ecx,[var1]

19 00a64b1d push ecx

20 00a64b1e call swap (0a61424h)

21 00a64b23 add esp,8

上面**再次證明了,引用與指標的行為完全一致,只是編譯器在編譯時對引用作了更嚴格的限制。

因為在在表示式中,使用引用實際上就像使用變數本身一樣,所以直接用sizeof是得不到引用本身的大小的。

double var = 42.0; 

double& ref = var;

cout << sizeof var << endl; // print 8

cout << sizeof ref << endl; // print 8

我們可以通過定義乙個只含有引用的類來解決這個問題:

1 class refclass 

6 };

7 8 cout << sizeof refclass << endl; // print 4

所以結論就是引用和指標一樣實際佔記憶體空間4個位元組。

正確結論:

不要用彙編結果來替代概念,引用不佔空間意思就是不佔物件空間,不表示不佔指標的少量空間。實際上指標是彙編工具實現引用的一種方式而已,而有的優化結果可能沒有代表自己的指標。

總而言之,引用就是引用,是這種概念,它為方便程式設計師使用,和方便彙編工具優化而產生。彙編怎麼實現和優化是彙編的事,至於出了什麼違反該概念的結果,是彙編的錯,而不是定義的錯,不要本末倒置。

你可以通過彙編來了解編譯器怎樣實現引用

引用 卻不應該用彙編來解釋 它只是乙個概念

贊同,引用只是編譯器之上,給出來的乙個抽象定義。介面的實現,由編譯器來決定!

仔細想想,確實如此,引用只是乙個概念,為我們提供了乙個介面。怎麼實現,由編譯器自己決定。

C 結構體中的引用和引用占用記憶體問題

答 並不會,引用的底層實現其實是乙個指向相同變數的指標,但是套用乙個老哥的話 指標資訊是占用記憶體的,這部分記憶體是系統自動分配的,不需要你來操心它被放在 通常我們說分配記憶體空間,是給你的變數內容使用的。你的變數可能是一串字元,一段文字,乙個數值。這些是真正需要你分配空間的地方。就像賓館一樣,來了...

C 記憶體占用問題

關於學習 完c語言中 變數的記憶體管理和結構 體變數所佔記憶體大小的問題後 到第二階段學 習c 面向對 象的程式設計 大家肯定會有 這樣的疑問 在c 中乙個類對 象究竟占用多少記憶體?最近關於此 問題進行了相關資 料整理,感覺 這裡解釋的 應該很清楚,感 興趣的同學 請網下看 很多c 書籍中都介紹過...

C 函式記憶體占用

乙個類的物件中是沒有關於普通成員函式的指標的slot,只有成員變數還有虛表指標,類的成員函式的 定義在pe檔案的 區,所以從程式載入時,就已經分配好了記憶體用於存放這些 執行時所需要的記憶體,比如棧 堆等等,則是 執行時才分配的 對於某個類的所有物件來說,類成員函式只在記憶體中有乙份拷貝,所有的物件...