返回指標變數的函式
阿新 • • 發佈:2019-02-19
原文出自http://blog.chinaunix.net/uid-27411029-id-3497902.html
[cpp] view plain copy print?
[cpp] view plain copy print?
- 原文自http://blog.chinaunix.net/uid-15014334-id-3533931.html
原文自http://blog.chinaunix.net/uid-15014334-id-3533931.html
[cpp]
view plain
copy
print?
- 一般的來說,函式是可以返回區域性變數的。 區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了。因此,如果函式返回的是區域性變數的值,不涉及地址,程式不會出錯。但是如果返回的是區域性變數的地址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯。準確的來說,函式不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。
- 1. 返回區域性變數的值
- 可以有兩種情況:返回區域性自動變數和區域性靜態變數,比如,
- int func()
- {
- int temp = 0; // 返回區域性自動變數的值
- return temp;
- }
- 區域性變數temp儲存在棧中,函式返回時會自動複製一份temp的copy給呼叫者,沒有問題。
- int func()
- {
- staticint a = 1; // 返回區域性靜態變數的值
- return a;
- }
- 區域性變數a儲存在靜態(全域性)儲存區中,從初始化後一直有效直到程式結束,僅分配一次記憶體,並且函式返回後,變數不會銷燬,沒有問題。
- vector<int
- {
- vector<int> 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()
- {
- staticint 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:
- [cpp] view plaincopy
- #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函式退出時,該該字串常量所在記憶體不會被回收,故能夠通過指標順利無誤的訪問。
- 2:
- [html] view plaincopy
- #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:
- [html] view plaincopy
- int func()
- {
- int a;
- ....
- return a; //允許
- }
- int * func()
- {
- int a;
- ....
- return &a; //無意義,不應該這樣做
- }
- 區域性變數也分區域性自動變數和區域性靜態變數,由於a返回的是值,因此返回一個區域性變數是可以的,無論自動還是靜態,
- 因為這時候返回的是這個區域性變數的值,但不應該返回指向區域性自動變數的指標,因為函式呼叫結束後該區域性自動變數
- 被拋棄,這個指標指向一個不再存在的物件,是無意義的。但可以返回指向區域性靜態變數的指標,因為靜態變數的生存
- 期從定義起到程式結束。
- 4:如果函式的返回值非要是一個區域性變數的地址,那麼該區域性變數一定要申明為static型別。如下:
- [html] view plaincopy
- #include <stdio.h>
- char *returnStr()
- {
- staticchar p[]="hello world!";
- return p;
- }
- int main()
- {
- char *str;
- str=returnStr();
- printf("%s\n", str);
- return 0;
- }
- 5: 陣列是不能作為函式的返回值的,原因是編譯器把陣列名認為是區域性變數(陣列)的地址。返回一個數組一般用返回指向這個陣列的指標代替,而且這個指標不能指向一個自動陣列,因為函式結束後自動陣列被拋棄,但可以返回一個指向靜態區域性陣列的指標,因為靜態儲存期是從物件定義到程式結束的。如下:
- [html] view plaincopy
- int* func( void )
- {
- staticint a[10];
- ........
- return a;
- }
- 6:返回指向堆記憶體的指標是可以的
- [html] view plaincop
- 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釋放記憶體。動態記憶體的生存期一直到程式設計師自己釋放。
一般的來說,函式是可以返回區域性變數的。 區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了。因此,如果函式返回的是區域性變數的值,不涉及地址,程式不會出錯。但是如果返回的是區域性變數的地址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯。準確的來說,函式不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。
1. 返回區域性變數的值
可以有兩種情況:返回區域性自動變數和區域性靜態變數,比如,
int func()
{
int temp = 0; // 返回區域性自動變數的值
return temp;
}
區域性變數temp儲存在棧中,函式返回時會自動複製一份temp的copy給呼叫者,沒有問題。
int func()
{
static int a = 1; // 返回區域性靜態變數的值
return a;
}
區域性變數a儲存在靜態(全域性)儲存區中,從初始化後一直有效直到程式結束,僅分配一次記憶體,並且函式返回後,變數不會銷燬,沒有問題。
vector<int> func()
{
vector<int> 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:
[cpp] view plaincopy
#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函式退出時,該該字串常量所在記憶體不會被回收,故能夠通過指標順利無誤的訪問。
2:
[html] view plaincopy
#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:
[html] view plaincopy
int func()
{
int a;
....
return a; //允許
}
int * func()
{
int a;
....
return &a; //無意義,不應該這樣做
}
區域性變數也分區域性自動變數和區域性靜態變數,由於a返回的是值,因此返回一個區域性變數是可以的,無論自動還是靜態,
因為這時候返回的是這個區域性變數的值,但不應該返回指向區域性自動變數的指標,因為函式呼叫結束後該區域性自動變數
被拋棄,這個指標指向一個不再存在的物件,是無意義的。但可以返回指向區域性靜態變數的指標,因為靜態變數的生存
期從定義起到程式結束。
4:如果函式的返回值非要是一個區域性變數的地址,那麼該區域性變數一定要申明為static型別。如下:
[html] view plaincopy
#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: 陣列是不能作為函式的返回值的,原因是編譯器把陣列名認為是區域性變數(陣列)的地址。返回一個數組一般用返回指向這個陣列的指標代替,而且這個指標不能指向一個自動陣列,因為函式結束後自動陣列被拋棄,但可以返回一個指向靜態區域性陣列的指標,因為靜態儲存期是從物件定義到程式結束的。如下:
[html] view plaincopy
int* func( void )
{
static int a[10];
........
return a;
}
6:返回指向堆記憶體的指標是可以的
[html] view plaincop
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釋放記憶體。動態記憶體的生存期一直到程式設計師自己釋放。
[cpp]
view plain
copy
print?
- 可以函式的引數表中傳入一個指標變數,然後區域性指標指向這個引數指標,則當函式返回時,由於引數指標所指的記憶體空間的壽命是主函式的壽命,函式返回時不會被釋放。
可以函式的引數表中傳入一個指標變數,然後區域性指標指向這個引數指標,則當函式返回時,由於引數指標所指的記憶體空間的壽命是主函式的壽命,函式返回時不會被釋放。
[cpp]
view plain
copy
print?
- int *fun(int *p)
- {
- int *a;
- *p=4;
- a=p;
- return a;
- }
- int main()
- {
- int a=2;
- int *p=&a;
- int *r=fun(p);
- cout<<*r;
- return 0;
- }
int *fun(int *p)
{
int *a;
*p=4;
a=p;
return a;
}
int main()
{
int a=2;
int *p=&a;
int *r=fun(p);
cout<<*r;
return 0;
}