C++程式設計經驗-返回區域性變數的討論

十日十乞001發表於2017-06-10
返回區域性變數沒問題 如果返回區域性變數有問題,函式的意義還有嗎? 全域性變數還用返回嗎? 

返回指向區域性變數的指標才有問題, 函式退棧之後,區域性變數消失, 指標將指向未知區域,所以出現問題。

返回區域性變數的引用也是絕對不可以的。 引用只是變數的一個別名,變數本體都不存在了,引用當然也沒有任何意義。 

還有,如果是堆空間,可以返回,即在函式中用new申請的空間,是可以返回的。

但是一般的情況下,好的風格是: 
儘量在同一個作用域內成對使用new   和delete,(也即不要返回堆空間),因為如果不是這樣,會是你的函式的介面變的不夠靈活, 試想每個呼叫你的函式的人還需要記得去delete掉你在函式中new的堆空間, 否則就會造成記憶體洩露。

返回啥其實都是值拷貝! 指標就是指標值拷貝,   不會拷貝被指向的內容。

永遠不要從函式中返回區域性自動變數的地址。如果你真的需要這樣操作。你可以在函式的參數列中傳入一個指標變數。然後將需要寫入的資料寫入到該指標變數指向的地址。由於該指標指向的變數,作用域在函式體 之外。因此不會在函式結束結束時被回收。


在C語言中為什麼說絕不能返回函式內區域性變數的地址?
在程式中,只在特定的過程或函式中可以訪問的變數,是相對與全域性變數而言的。
全域性變數也稱為外部變數,是在函式的外部定義的,它的作用域為從變數定義處開始,到本程式檔案的末尾。全域性變數全部存放在靜態儲存區,在程式開始執行時給全域性變數分配儲存區,程式行完畢就釋放。
區域性變數可以和全域性變數重名,但是區域性變數會遮蔽全域性變數。在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。

區域性變數的特點是:隨函式呼叫時建立 隨函式結束時析構(銷燬)。
設想,如果返回了一個區域性變數的指標,而恰好區域性變數偏偏又在函式結束後銷燬,但指標並沒有被銷燬,而是被返回,那也就是說,指標指向的正是一個被銷燬了的物件。
一般的來說,函式是可以返回區域性變數的。 區域性變數的作用域只在函式內部,在函式返回後,區域性變數的記憶體已經釋放了。因此,如果函式返回的是區域性變數的值,不涉及地址,程式不會出錯。但是如果返回的是區域性變數的地址(指標)的話,程式執行後會出錯。因為函式只是把指標複製後返回了,但是指標指向的內容已經被釋放了,這樣指標指向的內容就是不可預料的內容,呼叫就會出錯。準確的來說,函式不能通過返回指向棧記憶體的指標(注意這裡指的是棧,返回指向堆記憶體的指標是可以的)。

    下面以函式返回區域性變數的指標舉幾個典型的例子來說明:

1:

  1. #include <stdio.h>    
  2. char *returnStr()   
  3. {   
  4.     char *p="hello world!";   
  5.     return p;   
  6. }   
  7. int main()   
  8. {   
  9.     char *str;   
  10.     str=returnStr();   
  11.     printf("%s\n", str);   
  12.     return 0;   
  13. }  
這個沒有任何問題,因為"hello world!"是一個字串常量,存放在只讀資料段,把該字串常量存放的只讀資料段的首地址賦值給了指標,所以returnStr函式退出時,該該字串常量所在記憶體不會被回收,故能夠通過指標順利無誤的訪問。

2:

  1. #include <stdio.h>   
  2. char *returnStr()   
  3. {   
  4.     char p[]="hello world!";   
  5.     return p;   
  6. }   
  7. int main()   
  8. {   
  9.     char *str;   
  10.     str=returnStr();   
  11.     printf("%s\n", str);   
  12.     return 0;   
  13. }   
"hello world!"是區域性變數存放在棧中。當returnStr函式退出時,棧要清空,區域性變數的記憶體也被清空了,所以這時的函式返回的是一個已被釋放的記憶體地址,所以有可能列印出來的是亂碼。 

3:

  1. int func()  
  2. {  
  3.       int a;  
  4.       ....  
  5.       return a;    //允許  
  6. }                     
  7.   
  8. int * func()  
  9. {  
  10.       int a;  
  11.       ....  
  12.       return &a;    //無意義,不應該這樣做  
  13. }   

區域性變數也分區域性自動變數和區域性靜態變數,由於a返回的是值,因此返回一個區域性變數是可以的,無論自動還是靜態,

因為這時候返回的是這個區域性變數的值,但不應該返回指向區域性自動變數的指標,因為函式呼叫結束後該區域性自動變數

被拋棄,這個指標指向一個不再存在的物件,是無意義的。但可以返回指向區域性靜態變數的指標,因為靜態變數的生存

期從定義起到程式結束。

4:如果函式的返回值非要是一個區域性變數的地址,那麼該區域性變數一定要申明為static型別。如下:
  1. #include <stdio.h>   
  2. char *returnStr()   
  3. {   
  4.     static char p[]="hello world!";   
  5.     return p;   
  6. }   
  7. int main()   
  8. {   
  9.     char *str;   
  10.      str=returnStr();   
  11.     printf("%s\n", str);   
  12.   
  13.     return 0;   
  14. }   
5: 陣列是不能作為函式的返回值的,原因是編譯器把陣列名認為是區域性變數(陣列)的地址。返回一個陣列一般用返回指向這個陣列的指標代替,而且這個指標不能指向一個自動陣列,因為函式結束後自動陣列被拋棄,但可以返回一個指向靜態區域性陣列的指標,因為靜態儲存期是從物件定義到程式結束的。如下:

  1. int* func( void )  
  2. {  
  3.     static int a[10];  
  4.     ........  
  5.     return a;  
  6. }   

6:返回指向堆記憶體的指標是可以的

  1. char *GetMemory3(int num)  
  2. {  
  3. char *p = (char *)malloc(sizeof(char) * num);  
  4. return p;  
  5. }  
  6. void Test3(void)  
  7. {  
  8. char *str = NULL;  
  9. str = GetMemory3(100);  
  10. strcpy(str, "hello");  
  11. cout<< str << endl;  
  12. free(str);  
  13. }  
程式在執行的時候用 malloc 申請任意多少的記憶體,程式設計師自己負責在何時用 free釋放記憶體。動態記憶體的生存期由程式設計師自己決定,使用非常靈活。 

相關文章