《高質量c++和c編程》7.4 指針參數是如何傳遞內存的一節中寫道
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"); // 運行錯誤 }
無法返回內存,可以用如下方式
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); }
個人的理解就是,實際上指針傳遞仍然是一種值傳遞,只不過在參數是指針的時候,傳遞的是指針的副本,這樣你在地址上的操作實際就反映到了內存中,舉個例子來說,假設有一個函數
void fun(int *p) { p = new int; }
當用調用時fun(q),會產生實參的一個副本設為_p,函數體為副本_p分配了內存,實際上並未改變實參p,這就是GetMemory沒有成功的原因。相反,如果我們有如下函數
void fun(int *p) { *p = 3; }
在這個函數中,當發生實參調用的時候,仍然會產生實參的副本,但是注意這里不是改變副本,而是改變副本指向的內存中的內容,這里p是一個整形指針,在內存中占四個字節,副本和實參指向同一片內存,所以當你
在以副本為地址的內存內賦值3,實際也就是改變了實參指向的內存中的內容。
總結一下就是:指針傳遞仍然是值傳遞,所以我們在函數體內只有操作*p才會達到我們的指針傳遞要求,而不是操作p,這樣操作只在副本上,實際並不反映到實參指向的內存。
書中另一種方法是:
char *GetMemory3(int num) { char *p = (char *)malloc(sizeof return p; } void Test3(void) { char *str = NULL; str = GetMemory3(100); strcpy(str, "hello"); cout<< str << endl; free(str); }
實際是將堆的特性和return相結合,堆上分配的內存在函數不會釋放,而return實際返回的p的一個副本,但是這里的副本是一個指針,簡單的說是一個內存地址,而這個地址在函數結束后並沒有釋放,所以,我們可以繼續
使用。如果是普通的局部變量,return返回它的一個副本,隨后局部變量隨着函數的結束而被釋放,這在某些時候會引起麻煩,比如
char *GetString(void) { char p[] = "hello world"; return p; // 編譯器將提出警告 } void Test4(void) { char *str = NULL; str = GetString(); // str 的內容是垃圾 cout<< str << endl; }
至於return似乎還有東西說,一時想不起。。。
事情總有例外,今天小妞找我調試程序,發現了一件很奇特的事情,看代碼
void getArray(char **s, int N) { std::ifstream in("test.txt"); if (!in.is_open()) { std::cout<<"error"<<std::endl; } int i = 0; while(i < N) { s[i] = (char*)malloc(sizeof(char)*100); in.getline(s[i], 100); ++i; } in.close(); } int main() { char **s = (char **)malloc(sizeof(char) * 4); getArray(s, 4); for (int i=0; i<4; i++) { std::cout<<s[i]<<std::endl; } return 0; }
這個程序可以正確編譯執行。而下面代碼
void getArray1(char **s, int N) { // s = (char **)malloc(sizeof(char) * 4); s = new char*[4]; std::ifstream in("test.txt"); if (!in.is_open()) { std::cout<<"error"<<std::endl; } int i = 0; while(i < N) { // s[i] = (char*)malloc(sizeof(char)*100); s[i] = new char[100]; in.getline(s[i], 100); ++i; } in.close(); } int main() { char **s; getArray1(s, 4); for (int i=0; i<4; i++) { std::cout<<s[i]<<std::endl; } return 0; }
這個代碼確實在運行時出錯
分析了一下,個人認為雖然兩個函數的參數都是char **s,但是一個在main()中先分配,一個直接在getArray中分配,原因就在於此,getarray函數在main函數中先分配了內存,然后傳遞給它,雖然仍然是值傳遞,但是
s的元素是指針,getarray函數中在main函數分配的內存上完成了操作,所以當函數結束時,所有操作仍然保留下來。getarray1函數不同,它是在函數體內完整分配內存,然后施加操作的,相當於都在副本上,所有操作都不會
在函數結束后保留下來。
這是個人的一點理解,如有不對的地方還請指教。