这个c代码中这种奇怪的行为是什么?

时间:2013-12-19 23:19:03

标签: c stack heap

我不清楚以下两段代码如何显示不同的行为:

代码:

#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▒'

2 个答案:

答案 0 :(得分: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'
'

答案 1 :(得分:0)

seta()函数将str定义为指向字符串的指针。字符串本身是位于堆上的字符't','e','s','t','a','\0'数组。

_seta()函数定义了完全不同的东西:(一)指向(一)字符串的数组。数组本身位于堆栈上,这意味着一旦函数返回,数组就会超出范围(即得到thrashed)。字符串本身是另一个字符数组't','e','s','t','b','\0',它位于堆上,就像上面一样。

因此:调用_set(&b);获得指向未定义内存的指针。当你事先打电话给set(&a);时,一切似乎都有效的事实,纯粹是运气不好。