1. 程式人生 > >指標做形參,形參的傳遞詳解

指標做形參,形參的傳遞詳解

一、用二級指標作為函式引數,有兩種典型情況:

1.需要傳遞一級指標的陣列時:

例如標準Cmain函式:

int main(int argc, char*[] argv),陣列最高維可以退化,char*[] argv等價於char** argv。這裡argv代表命令列引數陣列。

2.需要對傳入的一級指標進行修改時:

例如:

void alloc_new_char_array(int n, char** t)

{

 *t = (char*)malloc(n * sizeof(t));

//二級指標**t既是入參也是出參。當我們需要提供一個封裝好的函式介面的來修改指標的時候,這種例子是很有借鑑意義的。

//在這個封裝函式內部修改指標,指標指向的區域可以是堆空間,也可以是全域性變數或靜態變數或字串常量(都儲存在靜態儲存器,在程式生命期內恆定不變),但一定不能使此函式體內的區域性變數,因為函式執行結束以後,存放在佔空間的區域性變數會被釋放空間。

}

類似地,一級指標作為引數則用來傳遞非指標陣列,以及對非指標物件進行修改。

二、什麼是實參和形參?

函式的引數分為形參和實參兩種。

形參出現在函式定義中,在整個函式體內都可以使用,實參出現在主調函式中,形參和實參的功能是作資料傳送。

發生函式呼叫時,主調函式把實參的值傳送給被調函式的形參從而實現主調函式向被調函式的資料傳送

實參:只傳遞數值,在過程(函式)中對之所進行的改動,不會造成原始變數值的改變

與之相對的是 地址引數,傳的是變數地址,所以在函式中的任何改動都會影響到主函式的實際引數

三、函式引數的傳遞問題(一級指標和二級指標) 

7.4指標引數是如何傳遞記憶體的?  

   如果函式的引數是一個指標,不要指望用該指標去申請動態記憶體。示例7-4-1中,Test函式的語句GetMemory(str,  200)並沒有使str獲得期望的記憶體,str依舊是NULL,為什麼?  

void  GetMemory(char  *p,  int  num)  

{  

           p  =  (char  *)malloc(sizeof(char)  *  num);  

}  

void  Test(void)  

{  

           char  *str  =  NULL;  

           GetMemory(str,  100);            //  str  仍然為  NULL              

           strcpy(str,  "hello");            //  執行錯誤  

}  

示例7-4-1  試圖用指標引數申請動態記憶體  

毛病出在函式GetMemory中。編譯器總是要為函式的每個引數製作臨時副本,指標引數p的副本是  _p,編譯器使  _p  =  p。如果函式體內的程式修改了_p的內容,就導致引數p的內容作相應的修改。這就是指標可以用作輸出引數的原因。在本例中,_p申請了新的記憶體,只是把_p所指的記憶體地址改變了,但是p絲毫未變。所以函式GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會洩露一塊記憶體,因為沒有用free釋放記憶體。  

如果非得要用指標引數去申請記憶體,那麼應該改用“指向指標的指標”,見示例7-4-2。  

void  GetMemory2(char  **p,  int  num)  

{  

           *p  =  (char  *)malloc(sizeof(char)  *  num);  

}  

void  Test2(void)  

{  

           char  *str  =  NULL;  

           GetMemory2(&str,  100);            //  注意引數是  &str,而不是str  

           strcpy(str,  "hello");              

           cout<<  str  <<  endl;  

           free(str);              

}  

示例7-4-2用指向指標的指標申請動態記憶體  

由於“指向指標的指標”這個概念不容易理解,我們可以用函式返回值來傳遞動態記憶體。這種方法更加簡單,見示例7-4-3

char  *GetMemory3(int  num)  

{  

           char  *p  =  (char  *)malloc(sizeof(char)  *  num);  

           return  p;  

}  

void  Test3(void)  

{  

           char  *str  =  NULL;  

           str  =  GetMemory3(100);              

           strcpy(str,  "hello");  

           cout<<  str  <<  endl;  

           free(str);              

}  

示例7-4-3  用函式返回值來傳遞動態記憶體  

用函式返回值來傳遞動態記憶體這種方法雖然好用,但是常常有人把return語句用錯了。這裡強調不要用return語句返回指向“棧記憶體”的指標,因為該記憶體在函式結束時自動消亡,見示例7-4-4。  

char  *GetString(void)  

{  

           char  p[]  =  "hello  world";  

           return  p;            //  編譯器將提出警告  

}  

void  Test4(void)  

{  

char  *str  =  NULL;  

str  =  GetString();            //  str  的內容是垃圾  

cout<<  str  <<  endl;  

}  

示例7-4-4  return語句返回指向“棧記憶體”的指標  

用偵錯程式逐步跟蹤Test4,發現執行str  =  GetString語句後str不再是NULL指標,但是str的內容不是“hello  world”而是垃圾。  

如果把示例7-4-4改寫成示例7-4-5,會怎麼樣?  

char  *GetString2(void)  

{  

           char  *p  =  "hello  world";  

           return  p;  

}  

void  Test5(void)  

{  

           char  *str  =  NULL;  

           str  =  GetString2();  

           cout<<  str  <<  endl;  

}  

示例7-4-5  return語句返回常量字串  

函式Test5執行雖然不會出錯,但是函式GetString2的設計概念卻是錯誤的。因為GetString2內的“hello  world”是常量字串,位於靜態儲存區,它在程式生命期內恆定不變。無論什麼時候呼叫GetString2,它返回的始終是同一個“只讀”的記憶體塊

轉載自:http://blog.sina.com.cn/s/blog_8b08096f01019uvq.html