要修改變量的值,需要使用變量類型的指針作為參數或者變量的引用。如果變量是一般類型的變量,例如int,則需要使用int 類型的指針類型int *作為參數或者int的引用類型int&。但是如果變量類型是指針類型,例如char*,那么需要使用該類型的指針,即指向指針的指針類型 char* *,或者該類型的引用類型char*&。
首先要清楚 不管是指針還是值傳入函數后都會創建一個副本,函數結束后值內容不能傳出來是因為值的副本,而傳入的值並沒被修改,指針能傳出來是因為我們修改的是指針指向的內容而不是指針指向的地址。
我們既然要舉例子 就找一個比較經典的實用例子。
在我們進行內存管理的時候,如果想創建一個分配空間的函數,函數中調用了malloc方法申請一塊內存區域。
先將一個錯誤的例子,如下:
void GetMemory1(char *p,int num)
{
p=malloc(sizeof(int)*num);
return;
}
void Test1()
{
char *p=NULL;
GetMemory(p);
}
上述例子 是很普通的 將一個指針作為參數來申請一個動態內存空間,可是這個程序是錯誤的。
錯誤的原因:
由於其中的*p實際上是Test1中p的一個副本,編譯器總是要為函數的每個參數制作臨時副本。在本例中,p申請了新的內存,只是把p所指向的內存地址改變了,但是Test1中p絲毫未變。因為函數GetMemory1沒有返回值,因此Test1中p並不指向申請的那段內存。
因為malloc的工作機制是在堆中尋找一塊可用內存區,返回指向被分配內存的指針。
所以這時p指向了這個申請的內存的地址。由於在指針作為傳入參數的時候會在函數體中創建一個副本指針_p
_p指針和p指針的聯系就是他們指向同一個內存區域,但是malloc的函數使得_p指向了另外一個內存區域,而這個內存區域並沒有座位傳出參數傳給p,
所以p並沒有發生任何改變,仍然為NULL。
如何才能解決上述問題呢?
使用下述辦法可以解決問題:
void GetMemory2(char **p,int num)
{
* p=malloc(sizeof(int)*num);
return;
}
void Test2()
{
char *p=NULL;
GetMemory(&p);
}
下面開始分析GetMemory2()和 Test2()的原理:
char **p 可以進行拆分(從左向右拆分) char* *p ,所以可以認為*p是一個char *的指針(注意這里不是p而是*p)。那么p的內容就是一個指向char*的指針的地址,換句話來說p是指向這個char*指針的指針。
從test2可以看出 p是一個char*的指針, &p則是這個char*指針的地址,換句話來說 &p是指向這個char*p的指針的指針,與GetMemory2()定義相符。所以在調用時候要使用&p而不是p。
在GetMemory2()中 *p=malloc(sizeof(int)*num);
其中*p保存了 這個分配的內存的地址,那么p就是指向這個分配的內存地址的指針。
其實在為什么要用指針的指針的道理很簡單:
因為VC內部機制是將函數的傳入參數都做一個副本,如果我們傳入的用來獲取malloc分配內存地址的副本變化了,而我們的參數並不會同步,除非使用函數返回值的方式才能傳出去。
所以我們就要找一個不變的可以並能用來獲取malloc內存地址的參數,
如果能夠創建另外一個指針A,這個指針指向GetMemory1(char * p,int ...)中的傳入參數指針p,那么 就算*p的內容變了,而p的地址沒變,我們仍然可以通過這個指針A來對應得到p的地址並獲得*p所指向的分配的內存地址,沒錯這個指針A就是本文想要講的指向(char *)指針的指針(char **)。
於是我們創建了一個char *的指針*p(注意這里不是char *的指針p),這個p作為傳入參數,在進入函數后,系統會為該指針創建一個副本_p,我們讓*_p指向malloc分配的內存的地址(注意這里是*_p而不是_p),_p作為指向這個分配的內存地址指針的指針,這樣在分配過程中_p並沒有變化。
另外注意在void Test2()中的char *p 的p是指向的是malloc分配的內存的地址,通過GetMemory2()函數后&p作為傳入參數沒有變化被返回回來,依據&p將p指針指向了真正分配的內存空間。
最后還要注意一個要點,void Test2()中 GetMemory(&p);和void GetMemory2(char **p,int num)這個函數的定義,很容易有一個迷惑,在使用處的變量和定義處的變量是如何一一對應的。
下面來做解釋:
不對應關系:其實這兩個p不是一個p,&p中的p是一個char *的指針,而char**p中的p卻是指向char *指針的指針。
對應關系:其實這個對應關系就是 在void Test2()調用的時候傳入參數&p是指向char *指針的指針,GetMemory2()定義中的char **p也是指向char*指針的指針,所以說 在調用時候傳入的參數和在定義時候使用的傳入參數必須是匹配的。
如果看了上面的文字還是比較混亂的話就用一句話來總結下重點:
1,VC的函數機制傳入的參數都是會創建一個副本 不管是指針還是值;如果是值A則直接創建另外一個值B,值B擁有和A相同的值;如果是傳入參指針C創建另外一個指針的副本D,那么D所指向的地址是和C相同的地址。
2,第1條總結可知指針傳參比值傳參的多出的功能是可以通過修改指針D所指向的地址的內容來講函數的運算結果告知指針C,因為C和D指向相同的地址。
3,第2跳總結的指針C和D所共用的指向地址可以是值類型,也可以是另外一個指針的地址(也就是上面所講的**)。