局部變量作爲返回值

看看下面的程序的輸出:

#include
char *returnStr()
{
     char *p="hello world!";
     return p;
}
int main()
{
     char *str;
     str=returnStr();
     printf("%s\n", str);
     return 0;
}

這個沒有任何問題,因爲"hello world!"是一個字符串常量,存放在靜態數據區,把該字符串常量存放的靜態數據區的首地址賦值給了指針,所以returnStr函數退出時,該該字符串常量所在內存不會被回收,故能夠通過指針順利無誤的訪問。

但是,下面的就有問題:

#include
char *returnStr()
{
     char p[]="hello world!";
     return p;
}
int main()
{
     char *str;
     str=returnStr();
     printf("%s\n", str);
     return 0;
}

"hello world!"是一個字符串常量,存放在靜態數據區,沒錯,但是把一個字符串常量賦值給了一個局部變量(char []型數組),該局部變量存放在棧中,這樣就有兩塊內容一樣的內存,這是與前着最本質的區別,當returnStr函數退出時,棧要清空,局部變量的內存 也被清空了,所以這時的函數返回的是一個已被釋放的內存地址,所以打印出來的是亂碼。

如果函數的返回值非要是一個局部變量的地址,那麼該局部變量一定要申明爲static類型。如下:

#include
char *returnStr()
{
     static char p[]="hello world!";
     return p;
}
int main()
{
     char *str;
      str=returnStr();
     printf("%s\n", str);

     return 0;
}

這個問題可以通過下面的一個例子來更好的說明:

#include
//返回的是局部變量的地址,該地址位於動態數據區,棧裏
char *s1()
{
     char p[]="Hello world!";
     printf("in s1 p=%p\n", p);
     printf("in s1: string's address: %p\n", &("Hello world!"));
     return p;
}
//返回的是字符串常量的地址,該地址位於靜態數據區
char *s2()
{
     char *q="Hello world!";
     printf("in s2 q=%p\n", q);
     printf("in s2: string's address: %p\n", &("Hello world!"));
     return q;
}
//返回的是靜態局部變量的地址,該地址位於靜態數據區
char *s3()
{
     static char r[]="Hello world!";
     printf("in s3 r=%p\n", r);
     printf("in s3: string's address: %p\n", &("Hello world!"));
     return r;
}
int main()
{
     char *t1, *t2, *t3;
     t1=s1();
     t2=s2();
     t3=s3();
     printf("in main:");
     printf("p=%p, q=%p, r=%p\n", t1, t2, t3);
     printf("%s\n", t1);
     printf("%s\n", t2);
     printf("%s\n", t3);
     return 0;
}

運行輸出結果:
in s1 p=0xbff92efb
in s1: string's address: 0x80486ac
in s2 q=0x80486ac
in s2: string's address: 0x80486ac
in s3 r=0x804998c
in s3: string

這 個結果正好應證了上面解釋,同時,還可是得出一個結論:字符串常量,之所以稱之爲常量,因爲它可一看作是一個沒有命名的字符串且爲常量,存放在靜態數據 區。這裏說的靜態數據區,是相對於堆、棧等動態數據區而言的。靜態數據區存放的是全局變量和靜態變量,從這一點上來說,字符串常量又可以稱之爲一個無名的 靜態變量,因爲"Hello world!"這個字符串在函數 s1和s2 中都引用了,但在內存中卻只有一份拷貝,這與靜態變量性質相當神似。

char *p = "abcdefg";//靜態存儲區

char p[] = "abcdefg"; // p本身是數組名了,數組裏放的字符串,是局部變量,內容是原來的靜態區域內容的拷貝!因此返回p實際上返回的局部變量地址而不是靜態存儲區地址,和上面不同!

函數返回局部變量,是返回局部變量的值。但指針(或地址)是一種特殊的值,所以返回局部指針變量需要特別注意。正確情況下,作爲函數返回值的局部指針,其所指向對象的作用域應該是調用者作用域、全局或靜態常量區;指向棧空間對象的指針作爲返回值,存在潛在的錯誤。

1.       返回局部值變量

    int num(void)  
    {  
             int a;  
             return a;  
    }  


函數返回時,將返回a的副本(值),然後a被註銷,所以不存在問題。

 

2.       返回局部指針變量

2.1 返回局部指針執行調用者作用域的對象



    char * strcpy( char *strDest, const char *strSrc )  
    {  
        assert( (strDest != NULL) &&(strSrc != NULL) );  
        char *strCopy = strDest;  
        while( (*strDest++ = * strSrc++) != ‘\0’ );  
        return strCopy;  
    }  

strCopy雖然是局部指針變量,但返回的strCopy副本實質是指向調用者調用時傳入的strDest地址對象,所以strcpy棧空間釋放後,strCopy被註銷但並不影響。

2.2返回局部指針執行常量、全局或靜態的作用域的對象



    char * retArr(void)  
    {  
        char *arr="hello,world\n";  
        //或static char arr[]="hello,world\n";   
             return arr;  
    }  


char *arr="hello,world\n"; "hello,world\n"存放在只讀常量區,非棧內存空間不受函數返回影響。

static char arr[]="hello,world\n"; "hello,world\n"存放在靜態存儲區(與全局變量相同),也不受函數返回的影響。

 

2.3 錯誤返回局部指針的情形:返回執行局部對象的指針

 

    char * retArr(void)  
    {  
        char arr[]="hello,world\n";  
        //或static char arr[]="hello,world\n";   
             return arr;  
    }  


char arr[]="hello,world\n"; arr[]所有元素保持在棧內存上,arr是個符號地址,沒有存儲空間。當棧內存都會被回收後,返回的arr的副本所指地址可能已經不再是"hello,world\n"(由系統釋放棧空間的時間決定),所以存在潛在的風險。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章