一直以為對於函數返回的指標瞭解得還可以,但是真實不用不知道,一用嚇一跳。今天在一篇部落格上面看到如下兩段代碼,部落格的作者給出了一個問題,但是並沒有解釋為什麼不同。自己通過實驗給出瞭解釋,但是不知道對不對,僅供參考!
下面是個錯誤的例子:
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之後就被破壞了