2013-12-19 64 views
0

我沒有明確的想法如何以下兩件代碼顯示不同的行爲:這個c代碼中的這個奇怪的行爲是什麼?

代碼:

#include <stdio.h> 

void set(char** addr) { 
    char* str = "testa"; 
    *addr = str; 
} 

void _set(char*** addr) { 
    char* arr[] = {"testb"}; 
    *addr = arr; 
} 

int main() { 
    char* a; 
    set(&a); 
    printf("'%s'\n", a); 
    printf("'%s'\n", a); 

    char** b; 
    _set(&b); 
    printf("'%s'\n",b[0]); 
    printf("'%s'\n",b[0]); 
} 

輸出:

testa 
testa 
testb 
testb 

當我刪除第一位的testa部分,代碼是:

void _set(char*** addr) { 
    char* arr[] = {"testb"}; 
    *addr = arr; 
} 

int main() { 
    char** b; 
    _set(&b); 
    printf("'%s'\n",b[0]); 
    printf("'%s'\n",b[0]); 
} 

輸出:

'testb' 
'UH▒▒AWE1▒AVAUATSH▒▒8▒E▒' 
+0

你回國(而不是返回值而是通過參數)一個指向局部變量的指針。這足以將周圍的一切都考慮在內......未定義的行爲。 –

+2

我建議你閱讀一本很好的C書,並且好好思考一下究竟是什麼指針。考慮變量的生命週期。 – orlp

回答

0

seta()功能str定義爲一個指針的字符串。字符串本身是位於堆上的字符數組't','e','s','t','a','\0'

_seta()函數定義了完全不同的東西:一個指向(一個)字符串的指針數組。該數組本身位於堆棧上,這意味着該數組一旦函數返回就會超出範圍(即得到thrashed)。字符串本身是另一個字符數組't','e','s','t','b','\0',位於堆上,就像上面一樣。

因此:調用_set(&b);獲得指向未定義內存的指針。預先稱爲set(&a);的事情似乎很有效,這純粹是運氣不好。

1

您正在經歷內存損壞。 main()中的代碼引用堆棧中的內存,當調用一個新函數時可能會損壞內存。 「TESTB」本身並沒有損壞,但ARR是(包含地址字符串「TESTB」的位置)

如果您做如下改變,它會工作:

char* arr[] = {"testb"}; /* Make arr global to fix the bug */ 

void _set(char*** addr) { 
    /* alternatively, you could make arr static here, static char* arr... */  

    *addr = arr; 
} 

有了足夠挖掘,應該可以解釋爲什麼它在第一種情況下工作,但不在第二種情況下,它將是確定性的和可重複的。例如,試試這個:

void _set(char*** addr) { 
    char pad[3]; // <-- Insert a 3 byte stack variable 
    char* arr[] = {"testb"}; 
    *addr = arr; 
} 

您應該看到現在不同的東西(嗯,這是否第二行看起來很熟悉?):

'testb' 
''%s' 
'