http://blog.sina.com.cn/s/blog_673ef8130100imsp.html
指標傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程中,被調函數的形式參數作為被調函數的局部變數處理,即在棧中開闢了記憶體空間以存放由主調函數放進來的實參的值,從而成為了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作為局部變數進行,不會影響主調函數的實參變數的值。(這裡是在說實參指標本身的地址值不會變)
而在引用傳遞過程中,被調函數的形式參數雖然也作為局部變數在棧中開闢了記憶體空間,但是這時存放的是由主調函數放進來的實參變數的地址(int &a的形式)。被調函數對形參的任何操作都被處理成間接定址,即通過棧中存放的地址訪問主調函數中的實參變數。正因為如此,被調函數對形參做的任何操作都影響了主調函數中的實參變數。
引用傳遞和指標傳遞是不同的,雖然它們都是在被調函數棧空間上的一個局部變數,但是任何對於引用參數的處理都會通過一個間接定址的方式操作到主調函數中的相關變數。而對於指標傳遞的參數,如果改變被調函數中的指標地址,它將影響不到主調函數的相關變數。如果想通過指標參數傳遞來改變主調函數中的相關變數,那就得使用指向指標的指標,或者指標引用。
為了進一步加深大家對指標和引用的區別,下面我從編譯的角度來闡述它們之間的區別:
程式在編譯時間分別將指標和引用添加到符號表上,符號表上記錄的是變數名及變數所對應地址。指標變數在符號表上對應的地址值為指標變數的地址值,而引用在符號表上對應的地址值為引用對象的地址值。符號表產生後就不會再改,因此指標可以改變其指向的對象(指標變數中的值可以改),而引用對象則不能修改。
最後,總結一下指標和引用的相同點和不同點:
★相同點:
●都是地址的概念;
指標指向一塊記憶體,它的內容是所指記憶體的地址;而引用則是某塊記憶體的別名。
★不同點:
●指標是一個實體,而引用僅是個別名;
●引用只能在定義時被初始化一次,之後不可變;指標可變;引用“從一而終”,指標可以“見異思遷”;
●引用沒有const,指標有const,const的指標不可變;(具體指沒有int&
const a這種形式,而const int& a是有 的, 前者指引用本身即別名不可以改變,這是當然的,所以不需要這種形式,後者指引用所指的值不可以改變)
●引用不可為空,指標可以為空白;
●“sizeof 引用”得到的是所指向的變數(對象)的大小,而“sizeof 指標”得到的是指標本身的大小;
●指標和引用的自增(++)運算意義不一樣;
●引用是型別安全的,而指標不是 (引用比指標多了類型檢查
| 一、引用的概念 引用引入了對象的一個同義字。定義引用的表示方法與定義指標相似,只是用&代替了*。 例如: Point pt1(10,10); Point &pt2=pt1; 定義了pt2為pt1的引用。通過這樣的定義,pt1和pt2表示同一對象。 需要特彆強調的是引用並不產生對象的副本,僅僅是對象的同義字。因此,當下面的語句執行後: pt1.offset(2,2); pt1和pt2都具有(12,12)的值。 引用必須在定義時馬上被初始化,因為它必須是某個東西的同義字。你不能先定義一個引用後才 初始化它。例如下面語句是非法的: Point &pt3; pt3=pt1; 那麼既然引用只是某個東西的同義字,它有什麼用途呢? 下面討論引用的兩個主要用途:作為函數參數以及從函數中返回左值。 二、引用參數 1、傳遞可變參數 傳統的c中,函數在調用時參數是通過值來傳遞的,這就是說函數的參數不具備傳回值的能力。 所以在傳統的c中,如果需要函數的參數具有傳回值的能力,往往是通過指標來實現的。比如,實現 兩整數變數值交換的c程式如下: void swapint(int *a,int *b) { int temp; temp=*a; a=*b; *b=temp; }
使用引用機制後,以上程式的c++版本為: void swapint(int &a,int &b) { int temp; temp=a; a=b; b=temp; } 調用該函數的c++方法為:swapint(x,y); c++自動把x,y的地址作為參數傳遞給swapint函數。 2、給函數傳遞大型物件 當大型物件被傳遞給函數時,使用引用參數可使參數傳遞效率得到提高,因為引用並不產生對象的 副本,也就是參數傳遞時,對象無須複製。下面的例子定義了一個有限整數集合的類: const maxCard=100; Class Set { int elems[maxCard]; // 集和中的元素,maxCard 表示集合中元素個數的最大值。 int card; // 集合中元素的個數。 public: Set () {card=0;} //建構函式 friend Set operator * (Set ,Set ) ; //重載運算子號*,用於計算集合的交集 用對象作為傳值參數 // friend Set operator * (Set & ,Set & ) 重載運算子號*,用於計算集合的交集 用對象的引用作為傳值參數 ... } 先考慮集合交集的實現 Set operator *( Set Set1,Set Set2) { Set res; for(int i=0;i<Set1.card;++i) for(int j=0;j>Set2.card;++j) if(Set1.elems[i]==Set2.elems[j]) { res.elems[res.card++]=Set1.elems[i]; break; } return res; } 由於重載運算子不能對指標單獨操作,我們必須把運算數聲明為 Set 類型而不是 Set * 。 每次使用*做交集運算時,整個集合都被複製,這樣效率很低。我們可以用引用來避免這種情況。 Set operator *( Set &Set1,Set &Set2) { Set res; for(int i=0;i<Set1.card;++i) for(int j=0;j>Set2.card;++j) if(Set1.elems[i]==Set2.elems[j]) { res.elems[res.card++]=Set1.elems[i]; break; } return res; } 三、引用傳回值 如果一個函數返回了引用,那麼該函數的調用也可以被賦值。這裡有一函數,它擁有兩個引用參數並返回一個雙精確度數的引用: double &max(double &d1,double &d2) { return d1>d2?d1:d2; } 由於max()函數返回一個對雙精確度數的引用,那麼我們就可以用max() 來對其中較大的雙精確度數加1: max(x,y)+=1.0; |
註:int &a中的&本質就是“取地址”運算,在函數void swapint(int &a,int &b)中,&a要看成一個整體,看成是x的地址,故a就是x本身了
有區別,
無論你傳值還是傳指標,函數都會產生一個臨時變數,
但傳引用時,不會產生臨時變數,
當你傳值時,只可以引用值而不可以改變值,但傳值引用時,可以改變值,
當你傳指標時,只可以改變指標所指的內容,不可以改變指標本身,但傳指標引用時,既可以改變指標所指的內容,又可以改變指標本身,
但傳引用主要是它不產生臨時變數,不進行傳回值copy等,速度快。
資料結構的二叉排序樹中有典型應用..
註:簡單一點可以這麼想,如果不用引用的話,被傳遞的參數本身是不能被修改的,
即使你傳遞的是指標,也只能修改指標指向的內容,不能修改指標本身。
如果要修改當前被傳遞的參數的話,要麼再加一級指標,要麼用引用。