2013-12-19 60 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' 
'