函式返回區域性變數問題
阿新 • • 發佈:2019-01-29
一般的來說,函式是可以返回區域性變數的,但是要注意幾種情況。 區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了。因此,如果函式返回的是區域性變數的值,不涉及地址,程式不會出錯。但是如果返回的是區域性變數的地址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯。準確的來說,函式不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。
1. 返回區域性變數的值可以有以下幾種情況:
(1)返回區域性自動變數
(2)區域性靜態變數
(3) 返回一個靜態的區域性變數的地址;
但是如果返回的是區域性變數的指標就出問題了,因為棧裡的變數在超過其作用域後或者函式返回後就銷燬了,不存在了,如下所示:
但是返回一個區域性字串的指標是不可行的!
(5)返回一個堆地址空間;
(6)返回一個區域性的物件;
1. 返回區域性變數的值可以有以下幾種情況:
(1)返回區域性自動變數
區域性變數temp儲存在棧中,函式返回時會自動複製一份temp的copy給呼叫者,沒有問題。int func() { int temp = 0; // 返回區域性自動變數的值 return temp; }
(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型別,因為靜態儲存期是程式從物件定義到程式結束。或者是全域性的陣列也行!!