從彙編角度看引用

2021-10-05 18:01:49 字數 3873 閱讀 3272

引用型別到底是什麼?它和指標有什麼關係?它本身占用記憶體空間嗎? 帶著這些疑問,我們來進行分析。 先看**:

#include

#include

using namespace std;

void main()

通過彙編檢視**如下:

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

可以知道x的位址為ebp-4,b的位址為ebp-8,因為棧內的變數記憶體是從高往低進行分配的。所以b的位址比x的低。

lea eax,[ebp-4]  這條語句將x的位址ebp-4放入eax暫存器

mov dword ptr [ebp-8],eax 這條語句將eax的值放入b的位址ebp-8中

所以從彙編層次來看,的確引用是通過指標來實現的。

下面我們通過程式來驗證,我們知道,在程式一層我們只要直接涉及到引用變數的操作,我們操作的總是被引用變數,即編譯器幫我們做了些手腳,總是在引用前面加上*。所以我們要讀取真正的「引用變數的值」,必須採取一定的策略,好吧,我們就按照變數在棧中分布的特點來繞過編譯器的這個特點。

#include

#include

using namespace std;

void main()

輸出結果為:&x=12ff7c,&y=12ff78,&b=12ff74,b=12ff7c

press any key to continue

void main()

輸出結果為::&x=12ff7c,&b=12ff7c.

但是我們可以間接通過&y-1來得到b的位址,從而得到b的值:*(&y-1)  從結果可以知道,b的值即x的位址,從而可以知道,從地層實現來看,引用變數的確存放的是被引用物件的位址,只不過,對於高階程式設計師來說是透明的,編譯器遮蔽了引用和指標的差別。

下面是程式的變數在記憶體棧中的分布,引用變數一樣也占用記憶體空間,而且應該是4個位元組的空間。

雖然從底層來說,引用的實質是指標,但是從高層語言級別來看,我們不能說引用就是指標,他們是兩個完全不同的概念。有人說引用是受限的指標,這種說法我不贊同,因為從語言級別上,指標和引用沒有關係,引用就是另乙個變數的別名。對引用的任何操作等價於對被引用變數的操作。從語言級別上,我們就不要去考慮它的底層實現機制啦,因為這些對你是透明的。所以在面試的時候,如果面試的人問到這個問題,可以先從語言級別上談談引用,深入的話就從底層的實現機制進行分析。而不能什麼條件沒有就說:引用就是指標,沒有差別,......之類的回答

以下內容摘自網上,覺得挺好的,借用過來

什麼是引用? 

物件的別名(另乙個名稱)。

引用經常用於「按引用傳遞(pass-by-reference)」:

void swap(int& i, int& j)

int main()

此處的 i 和 j 分別是main中的 x 和 y。換句話說,i 就是x —— 並非指向 x 的指標,也不是x 的拷貝,而是 x 本身。對 i 的任何改變同樣會影響x,反之亦然。

ok,這就是作為乙個程式設計師所認知的引用。現在,給你乙個不同的角度,這可能會讓你更糊塗,那就是引用是如何實現的。典型的情況下,物件 x 的引用i 是x 的機器位址。但是,當程式設計師寫 i++ 時,編譯器產生增加 x 的**。更詳細的來說,編譯器用來尋找x 的位址位並沒有被改變。c 程式設計師將此認為好像是 c 風格的按指標傳遞,只是句法不同 (1) 將 & 從呼叫者移到了被呼叫者處,(2)消除了*s。換句話說,c 程式設計師會將i 看作為巨集(*p),而 p 就是指向 x 的指標(例如,編譯器自動地將潛在的指標解除引用;i++被改變為(*p)++;i = 7 被自動地轉變成*p = 7)。

定義: 

1、引用: 

c++是c語言的繼承,它可進行過程化程式設計,又可以進行以抽象資料型別為特點的基於物件的程式設計,還可以進行以繼承和多型為特點的物件導向的程式設計。引用就是c++對c語言的重要擴充。引用就是某一變數的乙個別名,對引用的操作與對變數直接操作完全一樣。引用的宣告方法:型別識別符號 &引用名=目標變數名;引用引入了物件的乙個同義詞。定義引用的表示方法與定義指標相似,只是用&代替了*。 

2、指標: 

指標利用位址,它的值直接指向存在電腦儲存器中另乙個地方的值。由於通過位址能找到所需的變數單元,可以說,位址指向該變數單元。因此,將位址形象化的稱為「指標」。意思是通過它能找到以它為位址的記憶體單元。 

區別: 

1、指標有自己的一塊空間,而引用只是乙個別名; 

2、使用sizeof看乙個指標的大小是4,而引用則是被引用物件的大小; 

3、指標可以被初始化為null,而引用必須被初始化且必須是乙個已有物件的引用; 

4、作為引數傳遞時,指標需要被解引用才可以對物件進行操作,而直接對引用的修改都會改變引用所指向的物件; 

5、可以有const指標,但是沒有const引用; 

6、指標在使用中可以指向其它物件,但是引用只能是乙個物件的引用,不能 被改變; 

7、指標可以有多級指標(**p),而引用至於一級; 

8、指標和引用使用++運算子的意義不一樣; 

9、如果返回動態記憶體分配的物件或者記憶體,必須使用指標,引用可能引起記憶體洩露。

c++primer教材上說引用不能再改變繫結搞得物件,也就是引用再第二次賦值。

可是下面的程式能正常執行,不會出錯。這裡怎麼出現了引用賦值語句呢(語句[1])?是不是教材錯了?原因究竟是什麼呢? 

請看如下程式:

#include

void main()

首先想想程式執行結果應該是什麼呢?

vc6.0上執行後的結果是: 

i=5; j=5; k=5

分析: 

程式沒有錯誤,是正確的,但是並不能說明:引用能重新賦值。很明顯,引用是不能重新賦值的,只是理解上錯了! 

引用的賦值:是指引用初始化時,它的引用物件只能是變數,並且,一旦它指定為某乙個物件的引用後,就不能更改了。但是,可以用這個引用來改變它的物件的值,從而達到引用的目的——作為變數物件的別名。 

如上例,引用k初始化為i,即k從此以後一直是i的引用,若想讓k不再是i的引用而成為別的變數的引用那是不可能的。所以,接下來的一句「k=j;」就不能理解成:取消k是i的引用而將k作為j的引用。正確的理解應該是:利用引用k來改變它所指物件i的值,即相當於語句「k=5;」。若在上示例語句「k=j;」後加上一句「j=10」,結果將是:「i=5; j=10; k=5」,從這個結果就能很好理解了。 

所謂的引用的重新賦值,應該是: 

int x,y,z; 

int &x=y; 

&x=z; 

這種是對引用x,改變了它的指定物件,一開始是y的引用,之後,又重新說明是z的引用,這種引用的重新賦值是不允許的。

另外: 

常引用所引用的物件的值是不能更改的,即上述示例中若將語句「int& k=i;」更改為「const int& k=i;」,則在編譯時就會出現錯誤了。??? 

不能const 引用應該說的是不能 int & const b = a; 因為int &b 本就是int *const 型別,

可以const int &b = a;這樣後b就不能再賦值了。

其實可以這樣理解:

int &b = a;    -->  實質上是int *const  b  = &a;

引用的b是乙個const 指標,第一次指向某個物件後,下次不能再&b = 另外乙個物件,如果 b=c,實質是將a改變為c,並不是重新指向物件。

從彙編的角度看棧

大家都知道,棧區是儲存函式,區域性變數的一塊記憶體區域。那麼讓我們從彙編的角度,來看看函式的執行過程。首先,當我們使用pushl將資料入棧時,棧頂會移動,以容納新增加的值。實際上,我們能不斷將值入棧,棧會在記憶體中保持向下增長,知道存放 或資料的地方。那麼,我們如何知道棧頂位址呢?棧暫存器 esp總...

從彙編的角度看C 裡的引用和指標

c 裡有引用和指標,使用過c 的應該都知道它們的異同點。本文將從彙編的角度去觀察這2者的本質。使用也非常簡單,在左側編寫c 在右側就會直接顯示對應的組合語言,也可以根據需要的平台來選擇對應的編譯器 我們使用的c 如下,比較簡單 int main void 選擇的編譯器是x86 64 gcc 9.2,...

彙編角度看指標和引用的區別

一 定義上來看 引用是變數的別名,指標是指向變數的位址。引用不可以為空但是指標可以為空,所以引用必須在定義的時候必須初始化並且初始化後不能被改變引用的物件,但是指標可以該變自己的指向。引用必須引用有記憶體位址的量,如果是引用常量的話,必須使用常引用。常引用的實現方法是編譯器為常量開闢乙個臨時量的記憶...