指向臨時變量的指針的返回

一直以爲對於函數返回的指針瞭解得還可以,但是真實不用不知道,一用嚇一跳。今天在一篇博客上面看到如下兩段代碼,博客的作者給出了一個問題,但是並沒有解釋爲什麼不同。自己通過實驗給出瞭解釋,但是不知道對不對,僅供參考!

下面是個錯誤的例子:
char* get_str(void)
{
    char str[] = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}

下面這個例子沒有問題,大家知道爲什麼嗎?
char* get_str(void)
{
    char* str = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}
我在linux環境下面驗證了作者的說法,的確是那樣,上面一個有錯,下面一個正確。問題就出在一個用的是數組類型,而另外一個用的是指針類型(從側面亦可以看出並不是有些大學老師說的那樣char str[]等價於char* str),由於棧裏面的變量都是臨時的。當前函數執行完成時,相關的臨時變量和參數都被清除了。不能把指向這些臨時變量的指針返回給調用者,這樣的指針指向的數據是隨機的,會給程式造成不可預料的後果。但是指針卻有所不同,指針的地址在棧上,但是它所指向的內容卻是在堆上面,所以並沒有被清除。這就是爲什麼一個正確一個錯誤的原因。

其實這博客提前寫了一天,我並沒有發表,因爲怕自己理解得有錯,所以跟博客的作者寫信交流下了,下面貼出我們的信件內容,希望對需要的朋友有所幫助:

 

 問:
大家都知道,棧裏面的變量都是臨時的。當前函數執行完成時,相關的臨時變量和參數都被清除了。不能把指向這些臨時變量的指針返回給調用者,這樣的指針指向的數據是隨機的,會給程式造成不可預料的後果。
下面是個錯誤的例子:
char* get_str(void)
{
    char str[] = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}

下面這個例子沒有問題,大家知道爲什麼嗎?
char* get_str(void)
{
    char* str = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}
我在linux環境下跑了,對代碼做了點修改,相應的修改和運行結果如下:
#include <stdio.h>
char* get_str(void)
{
    char str[] = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%d/n", *p);
     printf("%d/n", *(p+1));
     return 0;
}
運行結果:
[root@localhost singnode]# ./ss
97
-12
 
#include <stdio.h>
char* get_str(void)
{
    char* str = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%d/n", *p);
     printf("%d/n", *(p+1));
     return 0;
}
運行結果:
[root@localhost singnode]# ./cc
97
98

對於這樣的運行結果是不是因爲指針的地址雖然是在棧上,但是它指向的內容卻是在堆上面,所以並沒有被清除。而數組的地址和內容都是在棧上面(首地址除外),所以出錯。而對於     printf("%d/n", *p);能夠輸出正確的97,是因爲首地址被當成指針來處理了,所以它的內容保存在堆上面,而其它的值保存在棧上面則被清除,從而產生了隨機值,如-12。

(在此解釋下我最初的理解,紅字部分,一開始我並不知道數組返回的p也是正確的值,只是在調用printf語句後p的值才被破壞掉了,所以這是我之前根據運行結果來做的解釋,在此糾正下。)

 

答:

int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%d/n", *p);
     printf("%d/n", *(p+1));
     return 0;
}
運行結果:
[root@localhost singnode]# ./cc
97
98

對於這樣的運行結果是不是因爲指針的地址雖然是在棧上,但是它指向的內容卻是在堆上面,所以並沒有被清除。而數組的地址和內容都是在棧上面(首地址除外),所以出錯。

答:是的。


而對於     printf("%d/n", *p);能夠輸出正確的97,是因爲首地址被當成指針來處理了,所以它的內容保存在堆上面,而其它的值保存在棧上面則被清除,從而產生了隨機值,如-12。

答:

    char* p = get_str();
此時p指向的內容還沒有破壞,你可以在調試器中看一下。
     printf("%d/n", *p);
此時因爲調用了printf,所以p就被破壞了。

     printf("%d/n", *(p+1));

即使再調用:
     printf("%d/n", *p);
結果也是錯誤的。

 

這是最後寫的回信:

非常感謝!我驗證了下,如下:
(gdb) print p
$2 = 0xbffff703 "dbcd"
(gdb) s
98
12      printf("%d/n", *p);
(gdb) print p
$3 = 0xbffff703 "��/017m"
(gdb)
在調用printf之前的p內容還沒有被破壞掉,但是調用printf之後就被破壞了

發佈了36 篇原創文章 · 獲贊 37 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章