詳解C++指針和引用

C++是在C語言的基礎上發展來的。C++除了有C語言的指針外,還增長一個新的概念——引用,初學者容易把引用和指針混淆一塊兒,面試或者筆試常常被考到。面試

要弄清楚這兩個概念,先從變量提及。數組

 

一:變量

什麼是變量呢?變量(variable)的定義在計算機科學中究竟是如何定義的?而後variable究竟是在內存中如何存儲值的呢?那麼跟着上面的問題,咱們來一一的解答。安全

首先最重要的,變量的定義,當你申明一個變量的時候,計算機會將指定的一塊內存空間和變量名進行綁定;這個定義很簡單,但其實很抽象,例如:int x = 5; 這是一句最簡單的變量賦值語句了, 咱們常說「x等於5」,其實這種說法是錯誤的,x僅僅是變量的一個名字而已,它自己不等於任何值的。這條語句的正確翻譯應該是:「將5賦值於名字叫作x的內存空間」,其本質是將值5賦值到一塊內存空間,而這個內存空間名叫作x。切記:x只是簡單的一個別名而已,x不等於任何值。其圖示以下:數據結構

 變量在內存中的操做實際上是須要通過2個步驟的:函數

1)找出與變量名相對應的內存地址。測試

2)根據找到的地址,取出該地址對應的內存空間裏面的值進行操做。this

 

二:指針

首先介紹到底什麼是指針?指針變量和任何變量同樣,也有變量名,和這個變量名對應的內存空間,只是指針的特殊之處在於:指針變量相對應的內存空間存儲的值剛好是某個內存地址。這也是指針變量區別去其餘變量的特徵之一。例如某個指針的定義以下spa

int x = 5;
int *ptr = &x;

ptr便是一個指正變量名。經過指針獲取這個指針指向的內存中的值稱爲dereference,間接引用。.net

其相對於內存空間的表示以下:翻譯

 

使用指針的優勢和必要性:

    指針可以有效的表示數據結構;

    能動態分配內存,實現內存的自由管理;

    能較方便的使用字符串;

    便捷高效地使用數組

    指針直接與數據的儲存地址有關,好比:值傳遞不如地址傳遞高效,由於值傳遞先從實參的地址中取出值,再賦值給形參代入函數計算;而指針則把形參的地址直接指向實參地址,使用時直接取出數據,效率提升,特別在頻繁賦值等狀況下(注意:形參的改變會影響實參的值!)

三:引用

引用是C++引入的新語言特性,是C++經常使用的一個重要內容之一。

引用(reference)在C++中也是常常被用到,尤爲是在做爲函數參數的時候,須要在函數內部修改更新函數外部的值的時候,能夠說是引用場景很是豐富。正確、靈活地使用引用,可使程序簡潔、高效。

我在工做中發現,許多人使用它僅僅是想固然,只是知道怎麼應用而已,而不去具體分析這個reference。

在某些微妙的場合,很容易出錯,究其起因,大多由於沒有搞清本源。

下面我就來簡單的分析一下這個reference。首先咱們必須明確的一點就是:reference是一種特殊的pointer。從這能夠看出reference在內存中的存儲結構應該跟上面的指針是同樣的,也是存儲的一塊內存的地址。例如reference的定義以下:

int x = 5;
int &y = x;

引用就是某一變量(目標)的一個別名,對引用的操做與對變量直接操做徹底同樣。

  引用的聲明方法:類型標識符 &引用名=目標變量名;

       上面的代碼,定義了引用y,它是變量x的引用,別名,這樣子,目標變量有兩個名稱,即該目標原名稱和引用名,且不能再把該引用名做爲其餘變量名的別名。

4、引用和指針有什麼區別?

(1)指針:指針是一個變量,只不過這個變量存儲的是一個地址,指向內存的一個存儲單元;而引用跟原來的變量實質上是同一個東西,只不過是原變量的一個別名而已。如:

int a=1;int *p=&a;
int a=1;int &b=a;

上面定義了一個整形變量和一個指針變量p,該指針變量指向a的存儲單元,即p的值是a存儲單元的地址。

    而下面2句定義了一個整形變量a和這個整形a的引用b,事實上a和b是同一個東西,在內存佔有同一個存儲單元。

(2)  引用不能夠爲空,當被建立的時候,必須初始化,初始化後就不會再改變了;而指針能夠是空值,能夠在任什麼時候候被初始化,指針的值在初始化後能夠改變,即指向其它的存儲單元。

(3)能夠有const指針,可是沒有const引用;

(4)指針能夠有多級,可是引用只能是一級(int **p;合法 而 int &&a是不合法的)

(5)」sizeof引用」獲得的是所指向的變量(對象)的大小,而」sizeof指針」獲得的是指針自己的大小;

(6)指針和引用的自增(++)運算意義不同;

(7)若是返回動態內存分配的對象或者內存,必須使用指針,引用可能引發內存泄漏;

(8)從內存分配上看,程序爲指針變量分配內存區域,而不爲引用分配內存區域,由於引用聲明時必須初始化,從而指向一個已經存在的對象。引用不能指向空值。

        注:標準沒有規定引用要不要佔用內存,也沒有規定引用具體要怎麼實現,具體隨編譯器 http://bbs.csdn.net/topics/320095541

(9)從編譯上看,程序在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變量名及變量所對應地址。指針變量在符號表上對應的地址值爲指針變量的地址值,而引用在符號表上對應的地址值爲引用對象的地址值。符號表生成後就不會再改,所以指針能夠改變指向的對象(指針變量中的值能夠改),而引用對象不能改。這是使用指針不安全而使用引用安全的主要緣由。從某種意義上來講引用能夠被認爲是不能改變的指針。

(10)不存在指向空值的引用這個事實,意味着使用引用的代碼效率比使用指針的要高。由於在使用引用以前不須要測試它的合法性。相反,指針則應該老是被測試,防止其爲空。

 

下面用通俗易懂的話來概述一下:

指針-對於一個類型T,T*就是指向T的指針類型,也即一個T*類型的變量可以保存一個T對象的地址,而類型T是能夠加一些限定詞的,如const、volatile等等。見下圖,所示指針的含義:

引用-引用是一個對象的別名,主要用於函數參數和返回值類型,符號X&表示X類型的引用。見下圖,所示引用的含義:

總之,能夠歸結爲"指針指向一塊內存,它的內容是所指內存的地址;而引用則是某塊內存的別名,引用不改變指向。"

 

5、指針傳遞和引用傳遞

在C++中,指針和引用常常用於函數的參數傳遞,然而,指針傳遞參數和引用傳遞參數是有本質上的不一樣的:

        指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程當中,被調函數的形式參數做爲被調函數的局部變量處理,即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,從而成爲了實參的一個副本。值傳遞的特色是被調函數對形式參數的任何操做都是做爲局部變量進行,不會影響主調函數的實參變量的值。(這裏是在說實參指針自己的地址值不會變)

        而在引用傳遞過程當中,被調函數的形式參數雖然也做爲局部變量在棧中開闢了內存空間,可是這時存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操做都被處理成間接尋址,即經過棧中存放的地址訪問主調函數中的實參變量。正由於如此,被調函數對形參作的任何操做都影響了主調函數中的實參變量。

        引用傳遞和指針傳遞是不一樣的,雖然它們都是在被調函數棧空間上的一個局部變量,可是任何對於引用參數的處理都會經過一個間接尋址的方式操做到主調函數中的相關變量。而對於指針傳遞的參數,若是改變被調函數中的指針地址,它將影響不到主調函數的相關變量。若是想經過指針參數傳遞來改變主調函數中的相關變量,那就得使用指向指針的指針,或者指針引用。

 

6、返回引用和返回指針

C++返回引用類型

    A& a(){ return *this;} 就生成了一個固定地址的指針,並把指針帶給你。

但A a() { return *this;}會生成一個臨時對象變量,並把這個臨時變量給你,這樣就多了一步操做。

當返回一個變量時,會產生拷貝。當返回一個引用時,不會發生拷貝,你能夠將引用看做是一個變量的別名,就是其餘的名字,引用和被引用的變量實際上是一個東西,只是有了兩個名字而已。

問題的關鍵是,當你想要返回一個引用而不是一個拷貝時,你要確保這個引用的有效性,好比:

        int & fun() { int a; a=10; return a; }

這樣是不行的,由於a會在fun退出時被銷燬,這時返回的a的引用是無效的。

這種狀況下,若是fun的返回類型不是int & 而是int就沒有問題了。

 

返回指針的話,誰調用該函數,誰負責接觸返回的指針。

 

全局變量,局部靜態變量,局部動態分配變量 均可以做爲函數返回值。 

局部自動變量不行

函數內部等局部變量,存儲在棧中的變量是不能做爲返回值的,雖然能夠讀取正確的值,可是這是一塊未分配的內存,當別的進程用到時就會出錯,這個指針至關於野指針。返回值能夠是局部動態分配的內存空間,這一部分分配在堆上,在主動釋放以前別的進程是沒法使用的內存區域。

不論是指針仍是引用都是如此。

 

 

7、特別之處const

爲何要提到const關鍵字呢?由於const對指針和引用的限定是有差異的:

常量指針VS常量引用

 

常量指針:指向常量的指針,在指針定義語句的類型前加const,表示指向的對象是常量。

定義指向常量的指針只限制指針的間接訪問操做,而不能規定指針指向的值自己的操做規定性。

 常量指針定義"const int* pointer=&a"告訴編譯器,*pointer是常量,不能將*pointer做爲左值進行操做。

 

常量引用:指向常量的引用,在引用定義語句的類型前加const,表示指向的對象是常量。也跟指針同樣不能對引用指向的變量進行從新賦值操做。

 

指針常量VS引用常量

在指針定義語句的指針名前加const,表示指針自己是常量。在定義指針常量時必須初始化!而這是引用與生俱來的屬性,無需使用const。

指針常量定義"int* const pointer=&b"告訴編譯器,pointer(地址)是常量,不能做爲左值進行操做,可是容許修改間接訪問值,即*pointer(地址所指向內存的值)能夠修改。

 

常量指針常量VS常量引用常量

常量指針常量:指向常量的指針常量,能夠定義一個指向常量的指針常量,它必須在定義時初始化。

定義"const int* const pointer=&c"

告訴編譯器,pointer和*pointer都是常量,他們都不能做爲左值進行操做。

 

而不存在所謂的"常量引用常量",由於引用變量就是引用常量。C++不區分變量的const引用和const變量的引用。程序決不能給引用自己從新賦值,使他指向另外一個變量,所以引用老是const的。若是對引用應用關鍵字const,起做用就是使其目標稱爲const變量。即

沒有:const double const& a=1;

只有const double& a=1;

  1. double b=1;
  2. constdouble& a=b;
  3. b=2;//正確
  4. a=3;//出錯error: assignment of read-only reference `a'

 

總結:有一個規則能夠很好的區分const是修飾指針,仍是修飾指針指向的數據——畫一條垂直穿過指針聲明的星號(*),若是const出如今線的左邊指針指向的數據爲常量;若是const出如今右邊指針自己爲常量。而引用自己就是常量,即不能夠改變指向。

相關文章
相關標籤/搜索