傳值,傳指針和傳引用區別和聯繫

C++寫了很多年,有一天,寫着寫着代碼。竟然自己發現對傳值,傳指針,傳引用這個每天都在用的傳遞方式的區別還不是很清楚。以爲自己懂了,其實還理解得還不夠深入,基礎還需要花時間琢磨。今天參考了很多篇博客和書籍做些總結。

其實,不用分爲三類,只有兩類即可。傳值和傳引用。爲什麼會出現傳地址(即傳指針)呢?本質就是大家一致對傳值和傳地址概念的理解錯誤導致,也是對指針的概念的理解錯誤導致。

概念

指針:指針就是一個變量,如果非要說是一個特殊的變量也不爲過,因爲指針的初始化和解引用等不同的操作方式而已。就內存的分佈來說,指針和一個變量在內存中存放是沒有任何區別的,無非指針存放的是變量的地址。

傳值:傳值無非就是實參拷貝傳遞給形參,單向傳遞(實參->形參),賦值完畢後實參就和形參沒有任何聯繫,對形參的修改就不會影響到實參。

傳地址:爲什麼說傳地址也是一種傳值呢?因爲傳地址是把實參地址的拷貝傳遞給形參。還是一句話,傳地址就是把實參的地址複製給形參。複製完畢後實參的地址和形參的地址沒有任何聯繫,對實參形參地址的修改不會影響到實參, 但是對形參地址所指向對象的修改卻直接反應在實參中,因爲形參指向的對象就是形參的對象。

傳引用:傳引用本質沒有任何實參的拷貝,一句話,就是讓另外一個變量也執行該實參。就是兩個變量指向同一個對象。這是對形參的修改,必然反映到實參上。

例:

#include<iostream>
using namespace std;

void Value(int n)
{
    cout << "Value(int n)"<< endl;
    cout << "{" << endl;
    cout << "   &n=" << &n << endl; 
    cout << "}" << endl;
    n++;
}

void Reference(int &n)
{
    cout << "Reference(int &n)" << endl;
    cout << "{" << endl;
    cout << "   n=" << n << "  &n=" << &n << endl;
    cout << "}" << endl;
    n++;
}

void Pointer(int *n)
{

    cout << "Pointer(int *n)" << endl;
    cout << "{" << endl;
    cout << "   n=" << n << "  &n=" << &n << endl;
    (*n)++;

    int b = 20;
    cout << "   b=" << b << "  n = &b" << endl;

    n = &b;
    cout << "   n=" << n << "  &n=" << &n << endl;
    (*n)++;
    cout << "}" << endl;
}

int main()
{
    int n = 10;
    cout << "n = " << 10 << "  &n=" << &n << endl<< endl;


    Value(n);
    cout << "after Value() n=" << n << endl << endl;

    Reference(n);
    cout << "after Reference() n=" << n << endl << endl;

    Pointer(&n);
    cout << "after Pointer() n=" << n << endl << endl ;

    system("pause");
    return true;
}

運行結果:
這裏寫圖片描述

分析:
值傳遞時函數操作的並不是實參本身,形參和實參是相互獨立的,所以對形參進行操作並不會改變實參的值。

引用傳遞操作地址是實參地址 ,形參相當於實參的一個別名,對它的操作就是對實參的操作。

指針傳遞時,可以通過指針操作實參,同樣可以改變實參的值。

總結
傳引用和傳指針看上去效果一樣的,但本質上有區別:

指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞的特點是被調函數對形式參數的任何操作都是作爲局部變量進行,不會影響主調函數的實參變量的值。
在值傳遞過程中,被調函數的形式參數作爲被調函數的局部變量,即在棧中開闢了內存空間以存放由主調函數放進來的實參的值,前面說過,值傳遞是單向傳遞(實參->形參),賦值完畢後實參就和形參沒有任何聯繫。那指針傳遞是怎麼通過這個局部變量訪問實參的呢,當然是通過局部變量中存儲的地址。
既然形參和實參是相互獨立的,在沒有任何修飾形參時,形參是可以被修改的,形參指針可以指向任何地方,而且修改後就無法再訪問到實參。例如Pointer函數中n = &b後,(*n)++不會再修改實參的值,這也是傳遞指針時通常會用const進行修飾的原因。
例如
這裏寫圖片描述

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

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

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

程序在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變量名及變量所對應地址。指針變量在符號表上對應的地址值爲指針變量的地址值,而引用在符號表上對應的地址值爲引用對象的地址值。符號表生成後就不會再改,因此指針可以改變其指向的對象(指針變量中的值可以改),而引用對象則不能修改。

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

★相同點:

●都是地址的概念;

指針指向一塊內存,它的內容是所指內存的地址;而引用則是某塊內存的別名。

★不同點:

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

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

●引用沒有const,指針有const,const的指針不可變;(具體指沒有int& const a這種形式,而const int& a是有 的, 前者指引用本身即別名不可以改變,這是當然的,所以不需要這種形式,後者指引用所指的值不可以改變)

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

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

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

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

以上內容是網上和書籍上找到的資料結合自己的理解寫的,有不對的地方,歡迎指教,一同學習。

發佈了112 篇原創文章 · 獲贊 162 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章