標籤:
(看到C++ Primer(P.225)“return reference to local object”時有點懵,然後找到了這篇博文。)
一般的來說,函數是可以返回局部變數的。 局部變數的範圍只在函數內部,在函數返回後,局部變數的記憶體已經釋放了。因此,如果函數返回的是局部變數的值,不涉及地址,程式不會出錯。但是如果返回的是局部變數的地址(指標)的話,程式運行後會出錯。因為函數只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,調用就會出錯。準確的來說,函數不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。
1. 返回局部變數的值
可以有兩種情況:返回局部自動變數和局部靜態變數,比如,
int func(){ int temp = 0; // 返回局部自動變數的值 return temp;}
局部變數temp儲存在棧中,函數返回時會自動複製一份temp的copy給調用者,沒有問題。
int func(){ static int a = 1; // 返回局部靜態變數的值 return a;}
局部變數a儲存在靜態(全域)儲存區中,從初始化後一直有效直到程式結束,僅分配一次記憶體,並且函數返回後,變數不會銷毀,沒有問題。
vector func(){ vector v; v.push_back(0); return v;}
返回的是v的值拷貝,沒有問題。
Person func(){ Person p1; p1.name = "test"; return p1;}
返回的也是值拷貝,會調用Person類的拷貝建構函式,沒有問題。
2. 返回局部變數的指標
int* func(){ int temp = 0; // 返回局部變數的地址 return &temp;}
前面討論過,局部變數temp儲存在棧中,函數返回時將已銷毀變數的地址返回給調用者,結果將是不可預知的。
int* func(){ static int temp = 1; return &temp;}
局部變數temp儲存在靜態儲存區,返回指向靜態儲存區變數的指標是可行的。
char* func(){ char *p = "test"; return p; // 返回指向常量字串的指標}
對於字串的特殊情況,由於字串test儲存在常量儲存區(不是靜態儲存區),因此函數返回一個指向常量的字串指標是可行的。
char* func(){ char str[] = "test"; return str; // 返回局部字串的指標}
這種情況下,str被初始化為字串局部變數,因此函數返回一個已銷毀的局部變數是不可行的。解決辦法就是將字串str聲明為static。
char* func(){ char *str = (char *)malloc(sizeof(char) * BUFFER_SIZE); strcpy(str, "test"); return str;}
這種情況下,函數返回一個指向堆記憶體的指標,由於堆儲存區由程式員手動管理,因此這種做法是可行的,但是要防止出現記憶體泄露,函數調用完後需要手動釋放記憶體。這裡的sizeof作用於指標返回的是指標類型的長度1byte,而如果作用於數組返回的則是數組的長度。
char *temp = NULL;temp = func();// some operation...free(temp);
3. 返回局部變數的引用
int& func(){ int temp = 0; // 返回局部變數的引用 return temp;}
由引用的概念可知,函數返回的是temp本身,而temp在函數返回後已銷毀,結果將是不可預知的。
補充:靜態全域變數和全域變數的區別
靜態全域變數只在當前檔案中可用,全域變數在其他檔案中也可用,需要用extern聲明。
全域變數和靜態變數如果沒有手動初始化,則預設由編譯器初始化為0。
1>
#include char *returnStr() { char *p="hello world!"; return p; } int main() { char *str; str=returnStr(); printf("%s\n", str); return 0; } 這個沒有任何問題,因為"hello world!"是一個字串常量,存放在唯讀資料區段,把該字串常量存放的唯讀資料區段的首地址賦值給了指標,所以returnStr函數退出時,該該字串常量所在記憶體不會被回收,故能夠通過指標順利無誤的訪問。
2>
#include <stdio.h> char *returnStr() { char p[]="hello world!"; return p; } int main() { char *str; str=returnStr(); printf("%s\n", str); return 0; } "hello world!"是局部變數存放在棧中。當returnStr函數退出時,棧要清空,局部變數的記憶體也被清空了,所以這時的函數返回的是一個已被釋放的記憶體位址,所以有可能列印出來的是亂碼。
3>
int func() { int a; .... return a; //允許 } int * func() { int a; .... return &a; //無意義,不應該這樣做 }
局部變數也分局部自動變數和局部靜態變數,由於a返回的是值,因此返回一個局部變數是可以的,無論自動還是靜態,
因為這時候返回的是這個局部變數的值,但不應該返回指向局部自動變數的指標,因為函數調用結束後該局部自動變數
被拋棄,這個指標指向一個不再存在的對象,是無意義的。但可以返回指向局部靜態變數的指標,因為靜態變數的生存
期從定義起到程式結束。
4>如果函數的傳回值非要是一個局部變數的地址,那麼該局部變數一定要申明為static類型。如下:
#include <stdio.h> char *returnStr() { static char p[]="hello world!"; return p; } int main() { char *str; str=returnStr(); printf("%s\n", str); return 0; } 5>數組是不能作為函數的傳回值的,原因是編譯器把數組名認為是局部變數(數組)的地址。返回一個數組一般用返回指向這個數組的指標代替,而且這個指標不能指向一個自動數組,因為函數結束後自動數組被拋棄,但可以返回一個指向靜態局部數組的指標,因為靜態儲存期是從對象定義到程式結束的。如下:
int* func( void ) { static int a[10]; ........ return a; }
6>返回指向堆記憶體的指標是可以的
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); }
程式在啟動並執行時候用 malloc 申請任意多少的記憶體,程式員自己負責在何時用 free釋放記憶體。動態記憶體的生存期由程式員自己決定,使用非常靈活。
【轉】函數返回局部變數