一,彙編分析引用:
引用類型到底是什麼?它和指針有什麼關係?它本身佔用內存空間嗎? 帶着這些疑問,我們來進行分析。 先看代碼:
#include <stdio.h>
#include <iostream>
using namespace std;
void main()
{
int x = 1;
int &b = x;
}
通過彙編查看代碼如下:
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中
上面兩條彙編的作用即:將x的地址存入變量b中,這不和將某個變量的地址存入指針變量是一樣的嗎?
所以從彙編層次來看,的確引用是通過指針來實現的。
下面我們通過程序來驗證,我們知道,在程序一層我們只要直接涉及到引用變量的操作,我們操作的總是被引用變量,即編譯器幫我們做了些手腳,總是在引用前面加上*。所以我們要讀取真正的“引用變量的值”,必須採取一定的策略,好吧,我們就按照變量在棧中分佈的特點來繞過編譯器的這個特點。
#include <stdio.h>
#include <iostream>
using namespace std;
void 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));
}
輸出結果爲:&x=12ff7c,&y=12ff78,&b=12ff74,b=12ff7c
Press any key to continue
void main()
{
int x = 1;
int &b = x;
printf("&x=%x,&b=%x\n",&x,&b);
}
輸出結果爲::&x=12ff7c,&b=12ff7c.
b的地址我們沒法通過&b獲得,因爲編譯器會將&b解釋爲:&(*b) =&x ,所以&b將得到&x。也驗證了對所有的b的操作,和對x的操作等同。
但是我們可以間接通過&y-1來得到b的地址,從而得到b的值:*(&y-1) 從結果可以知道,b的值即x的地址,從而可以知道,從地層實現來看,引用變量的確存放的是被引用對象的地址,只不過,對於高級程序員來說是透明的,編譯器屏蔽了引用和指針的差別。
下面是程序的變量在內存棧中的分佈,引用變量一樣也佔用內存空間,而且應該是4個字節的空間。
雖然從底層來說,引用的實質是指針,但是從高層語言級別來看,我們不能說引用就是指針,他們是兩個完全不同的概念。有人說引用是受限的指針,這種說法我不贊同,因爲從語言級別上,指針和引用沒有關係,引用就是另一個變量的別名。對引用的任何操作等價於對被引用變量的操作。從語言級別上,我們就不要去考慮它的底層實現機制啦,因爲這些對你是透明的。所以在面試的時候,如果面試的人問到這個問題,可以先從語言級別上談談引用,深入的話就從底層的實現機制進行分析。而不能什麼條件沒有就說:引用就是指針,沒有差別,......之類的回答
以下內容摘自網上,覺得挺好的,借用過來
什麼是引用?
對象的別名(另一個名稱)。
引用經常用於“按引用傳遞(pass-by-reference)”:
void swap(int& i, int& j)
{
int tmp = i;
i = j;
j = tmp;
}
int main()
{
int x, y;
// ...
swap(x,y);
}
此處的 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<iostream.h>
-
void main()
-
{
-
int i=1,j=5;
-
int& k=i;
-
k=j; //語句[1]
-
cout<<"i="<<i<<"; j="<<j<<"; k="<<k<<endl;
-
}
首先想想程序運行結果應該是什麼呢?
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,並不是重新指向對象。