天天看點

C++中指針和引用的差別

指針和引用主要有以下差別:

引用必須被初始化,但是不配置設定存儲空間。指針不聲明時初始化,在初始化的時候需要配置設定存儲空間。

引用初始化後不能被改變,指針可以改變所指的對象。

不存在指向空值的引用,但是存在指向空值的指針。

注意:引用作為函數參數時,會引發一定的問題,因為讓引用作參數,目的就是想改變這個引用所指向位址的内容,而函數調用時傳入的是實參,看不出函數的參數是正常變量,還是引用,是以可能引發錯誤。是以使用時一定要小心謹慎。

從概念上講。指針從本質上講就是存放變量位址的一個變量,在邏輯上是獨立的,它可以被改變,包括其所指向的位址的改變和其指向的位址中所存放的資料的改變。

而引用是一個别名,它在邏輯上不是獨立的,它的存在具有依附性,是以引用必須在一開始就被初始化,而且其引用的對象在其整個生命周期中是不能被改變的(自始至終隻能依附于同一個變量)。

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

指針傳遞參數本質上是 值傳遞的方式,它所傳遞的是一個位址值。值傳遞過程中,被調函數的形式參數作為被調函數的局部變量處理,即在棧中開辟了記憶體空間以存放由主調函數放進來的 實參的值,進而成為了實參的一個副本。值傳遞的特點是被調函數對形式參數的任何操作都是作為局部變量進行,不會影響主調函數的實參變量的值。

而在引用傳遞過程中, 被調函數的形式參數雖然也作為局部變量在棧中開辟了記憶體空間,但是這時存放的是由主調函數放進來的實參變量的位址。被調函數對形參的任何操作都被處理成間 接尋址,即通過棧中存放的位址通路主調函數中的實參變量。正因為如此,被調函數對形參做的任何操作都影響了主調函數中的實參變量。

引用傳遞和指針傳遞是 不同的,雖然它們都是在被調函數棧空間上的一個局部變量,但是任何對于引用參數的處理都會通過一個間接尋址的方式操作到主調函數中的相關變量。而對于指針 傳遞的參數,如果改變被調函數中的指針位址,它将影響不到主調函數的相關變量。如果想通過指針參數傳遞來改變主調函數中的相關變量,那就得使用指向指針的 指針,或者指針引用。

為了進一步加深大家對指針和引用的差別,下面我從編譯的角度來闡述它們之間的差別:

程式在編譯時分别将指 針和引用添加到符号表上,符号表上記錄的是變量名及變量所對應位址。指針變量在符号表上對應的位址值為指針變量的位址值,而引用在符号表上對應的位址值為 引用對象的位址值。符号表生成後就不會再改,是以指針可以改變其指向的對象(指針變量中的值可以改),而引用對象則不能修改。

最後,總結一下指針和引用的相同點和不同點:

相同點:

●都是位址的概念;

指針指向一塊記憶體,它的内容是所指記憶體的位址;而引用則是某塊記憶體的别名。

不同點:

●指針是一個實體,而引用僅是個别名;

●引用隻能在定義時被初始化一次,之後不可變;指針可變;引用“從一而終”,指針可以“見異思遷”;

●引用沒有const,指針有const,const的指針不可變;

●引用不能為空,指針可以為空;

●“sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身的大小;

●指針和引用的自增(++)運算意義不一樣;

●引用是類型安全的,而指針不是 (引用比指針多了類型檢查)

這幾天看重溫了下《高品質C/C++程式設計指南》和 《More Effective C++》對于裡面的引用和指針覺得寫得很精辟,同時在網上也找了些别人寫的總結,引用過來大家分享下。

    雖然使用引用和指針都可以間接通路另一個值,但他們之間有兩個重要差別:

引用總是指向某個對象,定義引用沒有初始化是錯誤的。

指派行為的差異,給引用指派修改的是該引用所關聯的對象的值,而并不是使引用與另一個對象關聯。引用一經初始化,就始終指向同一個特定對象。

    1. 都是位址的概念;

    指針指向一塊記憶體,它的内容是所指記憶體的位址;引用是某塊記憶體的别名。

    1. 指針是一個實體,而引用僅是個别名;

    2. 引用使用時無需解引用(*),指針需要解引用;

    3. 引用隻能在定義時被初始化一次,之後不可變;指針可變;

    引用“從一而終” ^_^

    4. 引用沒有 const,指針有 const,const 的指針不可變;

    5. 引用不能為空,指針可以為空;

    6. “sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身(所指向的變量或對象的位址)的大小;

    typeid(T) == typeid(T&) 恒為真,sizeof(T) == sizeof(T&) 恒為真,但是當引用作為成員時,其占用空間與指針相同(沒找到标準的規定)。

    7. 指針和引用的自增(++)運算意義不一樣;

    1. 引用在語言内部用指針實作(如何實作?)。

    2. 對一般應用而言,把引用了解為指針,不會犯嚴重語義錯誤。引用是操作受限了的指針(僅容許取内容操作)。

    引用是C++中的概念,初學者容易把引用和指針混淆一起。一下程式中,n 是m 的一個引用(reference),m是被引用物(referent)。

int m;  

int &n = m;  

    n 相當于m 的别名(綽号),對n 的任何操作就是對m 的操作。例如有人名叫王小毛,他的綽号是“三毛”。說“三毛”怎麼怎麼的,其實就是對王小毛說三道四。是以n 既不是m 的拷貝,也不是指向m 的指針,其實n就是m 它自己。

    以下示例程式中,k 被初始化為i 的引用。語句k = j 并不能将k 修改成為j 的引用,隻是把k 的值改變成為6.由于k 是i 的引用,是以i 的值也變成了6.

int i = 5;  

int j = 6;  

int &k = i;  

k = j; // k 和i 的值都變成了6;  

    上面的程式看起來象在玩文字遊戲,沒有展現出引用的價值。引用的主要功能是傳遞函數的參數和傳回值。C++語言中,函數的參數和傳回值的傳遞方式有三種:值傳遞、指針傳遞和引用傳遞。

    以下是“值傳遞”的示例程式。由于Func1 函數體内的x是外部變量n 的一份拷貝,改變x 的值不會影響n, 是以n 的值仍然是0.

void Func1(int x)  

{  

    x = x + 10;  

}  

int n = 0;  

Func1(n);  

cout << “n = ” << n << endl;// n = 0  

    以下是“指針傳遞”的示例程式。由于Func2 函數體内的x 是指向外部變量n 的指針,改變該指針的内容将導緻n 的值改變,是以n 的值成為10.

void Func2(int *x)  

    (* x) = (* x) + 10;  

⋯  

Func2(&n);  

cout << “n = ” << n << endl; // n = 10  

    以下是“引用傳遞”的示例程式。由于Func3 函數體内的x 是外部變量n 的引用,x和n 是同一個東西,改變x 等于改變n,是以n 的值成為10.

void Func3(int &x)  

//...  

Func3(n);  

對比上述三個示例程式,會發現“引用傳遞”的性質象“指針傳遞”,而書寫方式象“值傳遞”。實際上“引用”可以做的任何事情“指針”也都能夠做,為什麼還要“引用”

    這東西?

    答案是“用适當的工具做恰如其分的工作”。

    指針能夠毫無限制地操作記憶體中的如何東西,盡管指針功能強大,但是非常危險。

    就象一把刀,它可以用來砍樹、裁紙、修指甲、理發等等,誰敢這樣用?

    如果的确隻需要借用一下某個對象的“别名”,那麼就用“引用”,而不要用“指針”,以免發生意外。比如說,某人需要一份證明,本來在檔案上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那麼他就獲得了不該有的權利。

  指針與引用看上去完全不同(指針用操作符’*’和’->’,引用使用操作符’.’),但是它們似乎有相同的功能。指針與引用都是讓你間接引用其他對象。你如何決定在什麼時候使用指針,在什麼時候使用引用呢?

  首先,要認識到在任何情況下都不能用指向空值的引用。一個引用必須總是指向

某些對象。是以如果你使用一個變量并讓它指向一個對象,但是該變量在某些時候也可能不指向任何對象,這時你應該把變量聲明為指針,因為這樣你可以賦空值給

該變量。相反,如果變量肯定指向一個對象,例如你的設計不允許變量為空,這時你就可以把變量聲明為引用。

  “但是,請等一下”,你懷疑地問,“這樣的代碼會産生什麼樣的後果?”

char *pc = 0; // 設定指針為空值  

char& rc = *pc; // 讓引用指向空值  

  這是非常有害的,毫無疑問。結果将

是不确定的(編譯器能産生一些輸出,導緻任何事情都有可能發生),應該躲開寫出這樣代碼的人除非他們同意改正錯誤。如果你擔心這樣的代碼會出現在你的軟體

裡,那麼你最好完全避免使用引用,要不然就去讓更優秀的程式員去做。我們以後将忽略一個引用指向空值的可能性。

  因為引用肯定會指向一個對象,在C裡,引用應被初始化。

string& rs; // 錯誤,引用必須被初始化  

strings("xyzzy");  

string&rs = s; // 正确,rs指向s  

指針沒有這樣的限制。  

string*ps; // 未初始化的指針  

         // 合法但危險  

  不存在指向空值的引用這個事實意味着使用引用的代碼效率比使用指針的要高。因為在使用引用之前不需要測試它的合法性。

void printDouble(const double& rd)  

 cout<< rd; // 不需要測試rd,它  

}       // 肯定指向一個double值  

相反,指針則應該總是被測試,防止其為空:  

void printDouble(const double *pd)  

 if (pd)  

 {// 檢查是否為NULL  

  cout<< *pd;  

 }  

  指針與引用的另一個重要的不同是指針可以被重新指派以指向另一個不同的對象。但是引用則總是指向在初始化時被指定的對象,以後不能改變。

strings1("Nancy");  

strings2("Clancy");  

 string& rs = s1; // rs 引用 s1  

string *ps= &s1; // ps 指向 s1  

rs = s2; // rs 仍舊引用s1  

       // 但是s1的值現在是"Clancy"  

 ps = &s2; // ps 現在指向 s2;// s1 沒有改變  

總的來說,在以下情況下你應該使用 指針:

一是你考慮到存在不指向任何對象的可能(在這種情況下,你能夠設定指針為空);

二是你需要能夠在不同的時刻指向不同的對象(在這種情況下,你能改變 指針的指向)。如果總是指向一個對象并且一旦指向一個對象後就不會改變指向,那麼你應該使用引用。

還有一種情況,就是當你重載某個操作符時,你應該使用引用。最普通的例子是操作符[]。這個操作符典型的用法是傳回一個目标對象,其能被指派。

vector<int>v(10); //建立整形向量(vector),大小為10  

                 //向量是一個在标準C庫中的一個模闆(見條款35)   

v[5] = 10; // 這個被指派的目标對象就是操作符[]傳回的值  

  如果操作符[]傳回一個指針,那麼後一個語句就得這樣寫:

*v[5] = 10;  

  但是這樣會使得v看上去象是一個向量指針。是以你會選擇讓操作符傳回一個引用。(這有一個有趣的例外,參見條款30)

當你知道你必須指向一個對象并且不想改變其指向時,或者在重載操作符并為防止不必要的語義誤解時,你不應該使用指針。而在除此之外的其他情況下,則應使用指針。

C++ const引用詳解

(1)       在實際的程式中,引用主要被用做函數的形式參數--通常将類對象傳遞給一個函數.引用必須初始化. 但是用對象的位址初始化引用是錯誤的,我們可以定義一個指針引用。

(2)       一旦引用已經定義,它就不能再指向其他的對象.這就是為什麼它要被初始化的原因。

(3)       const引用可以用不同類型的對象初始化(隻要能從一種類型轉換到另一種類型即可),也可以是不可尋址的值,如文字常量。例如

上面,同樣的初始化對于非const引用是不合法的,将導緻編譯錯誤。原因有些微妙,需要适當做些解釋。

引用在内部存放的是一個對象的位址,它是該對象的别名。對于不可尋址的值,如文字常量,以及不同類型的對象,編譯器為了實作引用,必須生成一個臨時對象,引用實際上指向該對象,但使用者不能通路它。

例如:

編譯器将其轉換為:

同理:上面代碼

内部轉化為:

(4)       不允許非const引用指向需要臨時對象的對象或值,即,編譯器産生臨時變量的時候引用必須為const!!!!切記!!

(5)       ********對于const int *const & pi_ref = &iva; 具體的分析如下:*********

1.不允許非const引用指向需要臨時對象的對象或值

2.位址值是不可尋址的值

    3.于是,用const對象的位址來初始化一個指向指針的引用

const引用的語義到底是什麼?

最後,我們可能仍然不明白const引用的這個const的語義是什麼

const引用表示,試圖通過此引用去(間接)改變其引用的對象的值時,編譯器會報錯!

這并意味着,此引用所引用的對象也是以變成const類型了。我們仍然可以改變其指向對象的值,隻是不通過引用

下面是一個簡單的例子:

其中第10行,如果我們通過ir來改變val的值,編譯時會出錯。但是我們仍然可以通過val直接改變其值(第9行)

總結:const引用隻是表明,保證不會通過此引用間接的改變被引用的對象!

 另外,const既可以放到類型前又可以放到類型後面,放類型後比較容易了解:

微信公衆号:

<a target="_blank" href="https://yqfile.alicdn.com/img_e00999465d1c2c1b02df587a3ec9c13d.jpg">猿人谷</a>

如果您認為閱讀這篇部落格讓您有些收獲,不妨點選一下右下角的【推薦】

如果您希望與我交流互動,歡迎關注微信公衆号

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。

繼續閱讀