1. 程式人生 > >函式返回區域性變數問題

函式返回區域性變數問題

           一般的來說,函式是可以返回區域性變數的,但是要注意幾種情況。 區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了。因此,如果函式返回的是區域性變數的值,不涉及地址,程式不會出錯。但是如果返回的是區域性變數的地址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯。準確的來說,函式不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。
1. 返回區域性變數的值可以有以下幾種情況:
(1)返回區域性自動變數
int func()
{
    int temp = 0;   // 返回區域性自動變數的值
    return temp;
}
  區域性變數temp儲存在棧中,函式返回時會自動複製一份temp的copy給呼叫者,沒有問題。
(2)區域性靜態變數
int func()
{
    static int a = 1;   // 返回區域性靜態變數的值
    return a;
}
區域性變數a儲存在靜態(全域性)儲存區中,從初始化後一直有效直到程式結束,僅分配一次記憶體,並且函式返回後,變數不會銷燬,沒有問題。
(3) 返回一個靜態的區域性變數的地址;
int* func()
{
    static int temp = 1;
    return &temp;//區域性變數temp儲存在靜態儲存區,返回指向靜態儲存區變數的指標是可行的。
}
返回的靜態的區域性變數的地址是沒有問題的,因為靜態變數的生命週期是程式的結束,整個執行時間。
但是如果返回的是區域性變數的指標就出問題了,因為棧裡的變數在超過其作用域後或者函式返回後就銷燬了,不存在了,如下所示:
 int* func()
{
    int temp = 0;   // 返回區域性變數的地址
    return &temp;   //函式返回時將已銷燬變數的地址返回給呼叫者,結果將是不可預知的。
}
int& func()
{
    int temp = 0;   // 返回區域性變數的引用是不可行的,結果將不可預知;
    return temp;
}

因為引用返回的是區域性變數temp本身而不是copy的一份,所以結果是不可預知的!

引用的地址概念:指標指向一塊記憶體,它的內容是所指記憶體的地址;而引用則是某塊記憶體的別名。

          對(1)(2)(3)情況的小結:區域性變數也分為自動變數(auto)和靜態變數,由於(1)返回的是一個區域性變數的值是可以的,是copy的一份,而不是原先棧上的一份,但是不應該返回其地址和引用。因為函式結束後,該區域性變數棧上的變數被釋放拋棄,這個指標指向一個不存在的空間物件,是沒有意義的。但是由於靜態變數儲存在靜態儲存區,生命週期是整個程式執行的過程,所以可以返回區域性變數的值、引用或指標。

        如果函式的返回值非要是一個區域性變數的地址,那麼該區域性變數一定要申明為static型別。

(4)返回一個指向常量的字串指標
char* func()
{
    char *p = "Hello world!";
    return p;   // 返回指向常量字串的指標
}
對於字串的特殊情況,由於字串test儲存在常量儲存區(不是靜態儲存區),因此函式返回一個指向常量的字串指標是可行的。
但是返回一個區域性字串的指標是不可行的!
char* func()
{
    char str[] = "Hello world!";
    return str; // 返回區域性字串的指標
}
這種情況下,str被初始化為字串區域性變數,存放在區域性變數的棧中,因此函式返回一個已銷燬的區域性變數是不可行的。解決辦法就是將字串str宣告為static。
(5)返回一個堆地址空間; 
char* func()
{
    char buffer[20]="Hello world!";
    char *str = (char *)malloc(strlen(buffer)+1);
    strcpy(str, buffer);
    return str;
}
char *recv=func();
dosomething();
free(recv);
 返回一個堆記憶體空間是可行的,因為堆記憶體的生命週期是程式猿自己設定的,但是一定要記得釋放記憶體,不然造成記憶體的洩漏(memory leak)。使用非常靈活。
(6)返回一個區域性的物件;
Person func()
{
    Person p1;
    p1.name = "test";
    return p1;  // 返回的也是值拷貝,出現一個臨時物件,會呼叫Person類的拷貝建構函式,沒有問題。
}
vector func()
{
    vector v;
    v.push_back(0);
    return v;//返回的是v的值拷貝,臨時物件,沒有問題。
}
( 7)返回一個靜態數陣列
	int* func( void )  
	{  
	    static int a[10];  //靜態陣列;
	    ........  
	    return a;  
	}  
	#include <stdio.h>   
	char *returnStr()   
	{   
	    static char p[]="hello world!";   //靜態字串陣列
	    return p;   
	}   
      由於一般的自動陣列是不能作為函式的返回值的,原因是因為編譯器把陣列名認為是區域性變數(陣列)的地址,返回一個數組一般是返回這個陣列的首地址,用一個指標代替,而且這個指標是棧上的一個區域性變數,當函式結束後就自動釋放了,這樣就相當於返回了一個無效的指標。所以要返回一個區域性陣列需要將該陣列定義為static型別,因為靜態儲存期是程式從物件定義到程式結束。或者是全域性的陣列也行!!