1. 程式人生 > >返回指標變數的函式

返回指標變數的函式

原文出自http://blog.chinaunix.net/uid-27411029-id-3497902.html

[cpp] view plain copy print?
  1. 原文自http://blog.chinaunix.net/uid-15014334-id-3533931.html
原文自http://blog.chinaunix.net/uid-15014334-id-3533931.html
[cpp] view plain copy print?
  1. 一般的來說,函式是可以返回區域性變數的。 區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了。因此,如果函式返回的是區域性變數的值,不涉及地址,程式不會出錯。但是如果返回的是區域性變數的地址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯。準確的來說,函式不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。  
  2. 1. 返回區域性變數的值  
  3. 可以有兩種情況:返回區域性自動變數和區域性靜態變數,比如,  
  4. int func()  
  5. {  
  6.     int temp = 0;   // 返回區域性自動變數的值
  7.     return temp;  
  8. }  
  9. 區域性變數temp儲存在棧中,函式返回時會自動複製一份temp的copy給呼叫者,沒有問題。  
  10. int func()  
  11. {  
  12.     staticint a = 1;   // 返回區域性靜態變數的值
  13.     return a;  
  14. }  
  15. 區域性變數a儲存在靜態(全域性)儲存區中,從初始化後一直有效直到程式結束,僅分配一次記憶體,並且函式返回後,變數不會銷燬,沒有問題。  
  16. vector<int
    > func()  
  17. {  
  18.     vector<int> v;  
  19.     v.push_back(0);  
  20.     return v;  
  21. }  
  22. 返回的是v的值拷貝,沒有問題。  
  23. Person func()  
  24. {  
  25.     Person p1;  
  26.     p1.name = "test";  
  27.     return p1;  
  28. }  
  29. ?  
  30. 返回的也是值拷貝,會呼叫Person類的拷貝建構函式,沒有問題。  
  31. 2. 返回區域性變數的指標  
  32. int* func()  
  33. {  
  34.     int temp = 0;   // 返回區域性變數的地址
  35.     return &temp;  
  36. }  
  37. ?  
  38. 前面討論過,區域性變數temp儲存在棧中,函式返回時將已銷燬變數的地址返回給呼叫者,結果將是不可預知的。  
  39. int* func()  
  40. {  
  41.     staticint temp = 1;  
  42.     return &temp;  
  43. }  
  44. ?  
  45. 區域性變數temp儲存在靜態儲存區,返回指向靜態儲存區變數的指標是可行的。  
  46. char* func()  
  47. {  
  48.     char *p = "test";  
  49.     return p;   // 返回指向常量字串的指標
  50. }  
  51. ?  
  52. 對於字串的特殊情況,由於字串test儲存在常量儲存區(不是靜態儲存區),因此函式返回一個指向常量的字串指標是可行的。  
  53. char* func()  
  54. {  
  55.     char str[] = "test";  
  56.     return str; // 返回區域性字串的指標
  57. }  
  58. ?  
  59. 這種情況下,str被初始化為字串區域性變數,因此函式返回一個已銷燬的區域性變數是不可行的。解決辦法就是將字串str宣告為static。  
  60. char* func()  
  61. {  
  62.     char *str = (char *)malloc(sizeof(char) * BUFFER_SIZE);  
  63.     strcpy(str, "test");  
  64.     return str;  
  65. }  
  66. ?  
  67. 這種情況下,函式返回一個指向堆記憶體的指標,由於堆儲存區由程式設計師手動管理,因此這種做法是可行的,但是要防止出現記憶體洩露,函式呼叫完後需要手動釋放記憶體。這裡的sizeof作用於指標返回的是指標型別的長度1byte,而如果作用於陣列返回的則是陣列的長度。  
  68. char *temp = NULL;  
  69. temp = func();  
  70. // some operation...
  71. free(temp);  
  72. ?  
  73. 3. 返回區域性變數的引用  
  74. int& func()  
  75. {  
  76.     int temp = 0;   // 返回區域性變數的引用
  77.     return temp;  
  78. }  
  79. 由引用的概念可知,函式返回的是temp本身,而temp在函式返回後已銷燬,結果將是不可預知的。  
  80. 補充:靜態全域性變數和全域性變數的區別  
  81. 靜態全域性變數只在當前檔案中可用,全域性變數在其他檔案中也可用,需要用extern宣告。  
  82. 全域性變數和靜態變數如果沒有手動初始化,則預設由編譯器初始化為0。  
  83. 1:  
  84. [cpp] view plaincopy  
  85. #include <stdio.h>   
  86. char *returnStr()     
  87. {     
  88.     char *p="hello world!";     
  89.     return p;     
  90. }     
  91. int main()     
  92. {     
  93.     char *str;     
  94.     str=returnStr();     
  95.     printf("%s\n", str);     
  96.     return 0;     
  97. }    
  98. 這個沒有任何問題,因為"hello world!"是一個字串常量,存放在只讀資料段,把該字串常量存放的只讀資料段的首地址賦值給了指標,所以returnStr函式退出時,該該字串常量所在記憶體不會被回收,故能夠通過指標順利無誤的訪問。  
  99. 2:  
  100. [html] view plaincopy  
  101. #include <stdio.h>   
  102. char *returnStr()     
  103. {     
  104.     char p[]="hello world!";     
  105.     return p;     
  106. }     
  107. int main()     
  108. {     
  109.     char *str;     
  110.     str=returnStr();     
  111.     printf("%s\n", str);     
  112.     return 0;     
  113. }     
  114. "hello world!"是區域性變數存放在棧中。當returnStr函式退出時,棧要清空,區域性變數的記憶體也被清空了,所以這時的函式返回的是一個已被釋放的記憶體地址,所以有可能打印出來的是亂碼。   
  115. 3:  
  116. [html] view plaincopy  
  117. int func()    
  118. {    
  119.       int a;    
  120.       ....    
  121.       return a;    //允許  
  122. }                       
  123. int * func()    
  124. {    
  125.       int a;    
  126.       ....    
  127.       return &a;    //無意義,不應該這樣做  
  128. }     
  129. 區域性變數也分區域性自動變數和區域性靜態變數,由於a返回的是值,因此返回一個區域性變數是可以的,無論自動還是靜態,  
  130. 因為這時候返回的是這個區域性變數的值,但不應該返回指向區域性自動變數的指標,因為函式呼叫結束後該區域性自動變數  
  131. 被拋棄,這個指標指向一個不再存在的物件,是無意義的。但可以返回指向區域性靜態變數的指標,因為靜態變數的生存  
  132. 期從定義起到程式結束。  
  133. 4:如果函式的返回值非要是一個區域性變數的地址,那麼該區域性變數一定要申明為static型別。如下:  
  134. [html] view plaincopy  
  135. #include <stdio.h>   
  136. char *returnStr()     
  137. {     
  138.     staticchar p[]="hello world!";     
  139.     return p;     
  140. }     
  141. int main()     
  142. {     
  143.     char *str;     
  144.      str=returnStr();     
  145.     printf("%s\n", str);     
  146.     return 0;     
  147. }     
  148. 5: 陣列是不能作為函式的返回值的,原因是編譯器把陣列名認為是區域性變數(陣列)的地址。返回一個數組一般用返回指向這個陣列的指標代替,而且這個指標不能指向一個自動陣列,因為函式結束後自動陣列被拋棄,但可以返回一個指向靜態區域性陣列的指標,因為靜態儲存期是從物件定義到程式結束的。如下:  
  149. [html] view plaincopy  
  150. int* func( void )    
  151. {    
  152.     staticint a[10];    
  153.     ........    
  154.     return a;    
  155. }     
  156. 6:返回指向堆記憶體的指標是可以的  
  157. [html] view plaincop  
  158. char *GetMemory3(int num)    
  159. {    
  160. char *p = (char *)malloc(sizeof(char) * num);    
  161. return p;    
  162. }    
  163. void Test3(void)    
  164. {    
  165. char *str = NULL;    
  166. str = GetMemory3(100);    
  167. strcpy(str, "hello");    
  168. cout<< str << endl;    
  169. free(str);    
  170. }    
  171. 程式在執行的時候用 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?
  1. 可以函式的引數表中傳入一個指標變數,然後區域性指標指向這個引數指標,則當函式返回時,由於引數指標所指的記憶體空間的壽命是主函式的壽命,函式返回時不會被釋放。  
可以函式的引數表中傳入一個指標變數,然後區域性指標指向這個引數指標,則當函式返回時,由於引數指標所指的記憶體空間的壽命是主函式的壽命,函式返回時不會被釋放。
[cpp] view plain copy print?
  1. int *fun(int *p)  
  2. {  
  3.     int *a;  
  4.     *p=4;  
  5.     a=p;  
  6.     return a;  
  7. }  
  8. int main()  
  9. {  
  10.     int a=2;  
  11.     int *p=&a;  
  12.     int *r=fun(p);  
  13.     cout<<*r;  
  14.     return 0;  
  15. }  
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;
}