2016-10-31 47 views
0

據我所知,函數fun()結束後,變量'q'和地址超出了範圍。那麼,爲什麼代碼的輸出是「20 10」?爲什麼輸出「20 10」而不是垃圾值(懸掛指針)?

int *p2; 
void fun(int *ptr) 
{ 
    int q=10; 
    ptr=&q; 
    p2 = ptr; 
} 

int main() 
{ 
    int r=20; 
    int *p = &r; 
    fun(p); 
    printf("%d %d",*p,*p2); 
    return 0; 
} 
+3

這樣的功能因爲它是未定義的行爲。 –

+1

如何從非垃圾值中分辨垃圾值?你的'10'是一個垃圾值,和其他垃圾一樣好。 – AnT

+1

'10'是垃圾。 – chux

回答

0

這是純粹的undefined behavior

訪問無效的內存調用UB。 (只是因爲你可以寫代碼)你不應該這樣做。

20 10很可能被認爲是垃圾,因爲一旦你調用UB,輸出無法以任何方式證明。

FWIW,這個問題是說*p2訪問p2,提領p1是蠻好的,在C參數使用通按值傳遞的,所以任何改變ptr功能內不會影響實際變量ptr(不是*ptr)。

0

這是最糟糕的一種未定義行爲。代碼似乎正常工作。並且在所有測試和QA期間繼續這樣做。然後在您最重要的客戶最繁忙時期的午夜時間停止工作。

,但其他都表示UB = UB,UB包括工作顯然

3

正如其他人所說,節省了一個局部變量,然後嘗試取消引用函數返回後該地址是undefined behavior的地址。

這意味着你的程序可能會崩潰,它可能表現出奇怪的行爲,或者它看起來可能正常工作。對於相同的代碼或存在似乎無關的更改時,此行爲不需要從一個編譯器到下一個編譯器保持一致。也就是說,許多編譯器在返回後通常不會修改函數使用的堆棧部分。這是通常不需要的額外工作。所以在fun之後立即返回,它仍然包含它們的舊值的局部變量。

在調用printf時,指針p2在調用printf之前取消引用。由於在此之前沒有調用其他函數,所以最後一次調用fun的a值尚未被覆蓋。所以你讀了舊的價值。

如果在調用printf之前調用其他函數,先前被q佔用的內存位置將被覆蓋,以便您可能會看到其他值。

但是重申一下,這是未定義的行爲。並非所有的編譯器都需要以這種方式行事。例如,在高安全性環境中,編譯器可能會在函數返回後清除堆棧內存,以便該函數使用的敏感數據無法恢復。

+0

另一方面,在許多嵌入式平臺上,函數返回後發生的中斷可能會破壞該函數以前使用的堆棧幀;因爲中斷通常會在不可預知的時間到達,這意味着在大多數情況下,對於以前的堆棧幀的內容我們不能說任何有用的信息。 – supercat

0

正如別人所說,你有未定義的行爲,所以輸出可以是任何東西。

但是,除了未定義的行爲,你有一個值得關注的錯誤。

代碼的重寫,以避免UB可能是:

void fun(int *ptr){ 
    int q=10; 
    ptr=&q; 
} 

int main(){ 
    int r=20; 
    int *p = &r; 
    fun(p); 
    printf("%d",*p); 
    return 0; 
} 

此代碼是確定和輸出將是20。重點在於指針p是按值傳遞的。所以當函數返回時,函數內部所做的任何更改都會丟失。換句話說,主要的p沒有改變,仍然指向r

如果要更改某個函數中的指針值,請將其作爲雙指針傳遞。像void fun(int **ptr),像*ptr = ...一樣分配它,並調用像fun(&p)