C++函數的傳入參數是指針的指針(**)的詳解

要修改變量的值,須要使用變量類型的指針做爲參數或者變量的引用。若是變量是通常類型的變量,例如int,則須要使用int 類型的指針類型int *做爲參數或者int的引用類型int&。可是若是變量類型是指針類型,例如char*,那麼須要使用該類型的指針,即指向指針的指針類型 char* *,或者該類型的引用類型char*&。函數

 

首先要清楚  無論是指針仍是值傳入函數後都會建立一個副本,函數結束後值內容不能傳出來是由於值的副本,而傳入的值並沒被修改,指針能傳出來是由於咱們修改的是指針指向的內容而不是指針指向的地址。指針

咱們既然要舉例子 就找一個比較經典的實用例子。內存

 

在咱們進行內存管理的時候,若是想建立一個分配空間的函數,函數中調用了malloc方法申請一塊內存區域。編譯器

先將一個錯誤的例子,以下:同步

void GetMemory1(char *p,int num)內存管理

{
    p=malloc(sizeof(int)*num);編譯

   return;test

}變量

 

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所共用的指向地址能夠是值類型,也能夠是另一個指針的地址(也就是上面所講的**)。

相關文章
相關標籤/搜索