C++中引用和指针的不同
引用,其本質(zhì)就是指針,將它用在一些特別的場(chǎng)合,會(huì)比指針更簡潔,更方便。具體說來,指針多用于動(dòng)態(tài)內(nèi)存管理和對(duì)數(shù)組的操作等,C風(fēng)格的代碼接收和返回指針;引用則往往用在接收和返回類類型的名字空間域函數(shù)或類域函數(shù),以避免類對(duì)象的復(fù)制開銷。但是請(qǐng)注意,引用畢竟不完全等同于指針,它們有一些差異:
1、引用必須初始化,指針則不然。
int *pi;//可以
int &ri;//不行,未初始化
int i;int &ri=i;//可以
2、引用不能為空引用,指針則不然。
int *pi=NULL;//可以
int *pi=NULL;int &ri=*pi;//會(huì)引發(fā)問題。
3、引用在初始化后,不能再引用其他對(duì)象,指針則不然。
void f(int &ri)
{
??? int i=3;
??? ri=4;//改變所引用的實(shí)參
??? ri=&i;//錯(cuò)誤,類型不符
??? ri=i;//ri并未轉(zhuǎn)為引用局部變量i
}
雖然每次調(diào)用f時(shí),都有可能會(huì)使形參ri,引用不同的實(shí)參,但是在f函數(shù)內(nèi),無法在調(diào)用處,引用初始化后,再改變其所引用的對(duì)象。顯然包括引用在內(nèi),在很多時(shí)候,初始化與賦值運(yùn)算符的語義有很大區(qū)別。
4、引用在必要時(shí)實(shí)現(xiàn)為指針,但是常規(guī)語法取不到這個(gè)內(nèi)部指針的地址。
int i=3;int *pi=&i;//i的值為3,地址為&i;pi的值為&i,地址為&pi
int i=3;int &ri=i;//i的值為3,地址為&i;ri的值為3,地址為&i,初始化后ri和i基本為同義語。
實(shí)際上,當(dāng)引用ri初始化為i后,語言實(shí)現(xiàn)可能只將ri優(yōu)化處理成i的別名,而不將其實(shí)現(xiàn)為指針;也可能使用一個(gè)內(nèi)部指針來完成工作。比如第3條中的f函數(shù)的形參ri,每次具體調(diào)用函數(shù)時(shí),都可能會(huì)傳入不同地址的實(shí)參。因?yàn)樵O(shè)計(jì)引用就是要避免對(duì)象復(fù)制的,因此它會(huì)壓入堆棧一個(gè)指向被引用類型的指針。有可能這樣來實(shí)現(xiàn):
int i=3;
int &ri=i;//可實(shí)現(xiàn)為int *temp_pi=&i;以后每次源程序中使用ri時(shí),就會(huì)被相應(yīng)替換成*temp_pi
即使實(shí)現(xiàn)為這樣的內(nèi)部指針,也無法用正常的語法來取得它的地址。因?yàn)榘凑丈线叺姆治?#xff0c;初始化后,取地址操作&ri將等價(jià)于&*temp_pi。那么這對(duì)用戶來說,有什么會(huì)造成影響的不同嗎?來看看一種不太常見的情況:可變個(gè)數(shù)參數(shù)表的省略號(hào)...前一個(gè)參數(shù),如果是引用類型,將無法正常工作;如果是指針類型或簡單類型等,則不會(huì)有問題。如有以下聲明:
void f(int i,...);
則在實(shí)現(xiàn)代碼中,可以使用C標(biāo)準(zhǔn)庫的stdarg.h來取得可變個(gè)數(shù)部分的參數(shù)。有興趣的話可以去看看stdarg.h的具體代碼,它是用宏來完成的,針對(duì)不同的環(huán)境,采用了相應(yīng)的手段。我用一段功能類似的偽代碼來說明一下:
void f(int i,...)//此函數(shù)用i來表示可變個(gè)數(shù)參數(shù)的個(gè)數(shù)
{
??? int ix;
??? void *base=&i;
??? for(ix=0;ix<i;ix++)
??? {
??????? base+=sizeof(int);//或者依實(shí)現(xiàn)為base-=sizeof(int)
??????? cout<<"第"<<ix+1<<"個(gè)int參數(shù)的值為:";
??????? cout<<*((int *)base)<<endl;
??? }
}
?
如果有調(diào)用f(5,1,2,3,4,5);則5,4,3,2,1,5將從右到左依次被壓入堆棧,然后調(diào)用f函數(shù)。在f函數(shù)內(nèi)部來看,那幾個(gè)未命名的可變參數(shù),是每個(gè)占一個(gè)sizeof(int)大小,一個(gè)接一個(gè)挨著放在堆棧上的,并且局部變量i指向了堆棧頂?shù)奈恢谩_@時(shí)可以用相對(duì)于堆棧頂位置的位移量來取得每一個(gè)局部變量。當(dāng)函數(shù)返回時(shí),由調(diào)用點(diǎn)來清理堆棧。這樣,f(5,1,2,3,4,5);和f(3,10,11,12);均可正確調(diào)用和返回,因?yàn)槊刻幷{(diào)用均知道自己用了幾個(gè)參數(shù),所以它可以在函數(shù)返回后,正確地在堆棧上彈出參數(shù)(其缺點(diǎn)就是每處調(diào)用都要自己清理,這樣方式的程序長度,要比清理只出現(xiàn)在被調(diào)函數(shù)返回處的方式,要長一些)。這就是所謂的cdecl調(diào)用約定。然而,如果...前的參數(shù)為引用類型,如:
void f(const int &ri,...);//如果使用非左值進(jìn)行調(diào)用,引用必須為const
調(diào)用f(5,1,2,3,4,5)將會(huì)發(fā)生什么呢?
因?yàn)閕nt &ri是個(gè)引用參數(shù),所以實(shí)際上會(huì)是:
push 5;
push 4;
push 3;
push 2;
push 1;
const int temp_i=5;
const int *temp_pi=&temp_i;
push temp_pi;
也就是說,和5個(gè)可變個(gè)數(shù)參數(shù)并排挨著的實(shí)參ri,在實(shí)際調(diào)用時(shí),會(huì)被處理成向堆棧壓進(jìn)一個(gè)const int *類型的地址值。那么在f函數(shù)內(nèi)的代碼中,void *base=&ri;又將指向何處呢?按前面的分析,&ri會(huì)被處理成&*temp_pi,其結(jié)果就是temp_pi的值,temp_i的地址。試圖取回和1、2、3、4、5挨著的這個(gè)內(nèi)部指針的地址,其結(jié)果卻是內(nèi)部指針temp_pi的值,temp_i的地址,它并不在可變個(gè)數(shù)參數(shù)的1、2、3、4、5中1的旁邊。所以調(diào)用將無法正常取得...部分的參數(shù)。
發(fā)表于 @ 2009年03月01日 14:19:00 | 評(píng)論( 0 ) | 編輯| 舉報(bào)| 收藏
新一篇:精解C++的switch語句
本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/myliupp/archive/2009/03/01/3947023.aspx
總結(jié)
以上是生活随笔為你收集整理的C++中引用和指针的不同的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 精解C++的switch语句
- 下一篇: Visual C++设计UDP协议通讯示