傳值,傳指標和傳引用區別和聯絡
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 指標”得到的是指標本身的大小;
●指標和引用的自增(++)運算意義不一樣;
●引用是型別安全的,而指標不是 (引用比指標多了型別檢查)
以上內容是網上和書籍上找到的資料結合自己的理解寫的,有不對的地方,歡迎指教,一同學習。