1. 程式人生 > >C語言中函式的思考(可以返回區域性變數嗎)

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. int * func()  
  8. {  
  9.       int a;  
  10.       ....  
  11.       return &a;    //無意義,不應該這樣做  
  12. }   

區域性變數也分區域性自動變數和區域性靜態變數,由於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.     return 0;   
  13. }   
5: 陣列是不能作為函式的返回值的,原因是編譯器把陣列名認為是區域性變數(陣列)的地址。返回一個數組一般用返回指向這個陣列的指標代替,而且這個指標不能指向一個自動陣列,因為函式結束後自動陣列被拋棄,但可以返回一個指向靜態區域性陣列的指標,因為靜態儲存期是從物件定義到程式結束的。如下:
  1. int* func( void )  
  2. {  
  3.     static int a[10];  
  4.     ........  
  5.     return a;  
  6. }   

寫的很好! 但是第5點裡,“返回一個數組一般用返回指向這個陣列的指標代替”, 這裡應該是返回指向陣列首元素的的指標而不是陣列的指標吧。

  1. char *getClassName(void)  
  2. {  
  3.     staticchar className[50] = {0};  
  4.     ......  
  5.     return &className[0];  
  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釋放記憶體。動態記憶體的生存期由程式設計師自己決定,使用非常靈活。