了解结构中的指针行为

时间:2018-08-06 19:42:45

标签: c pointers struct

我用单个字段和一个初始化函数定义一个结构,如下所示:

typedef struct{
  int* field;
}myStruct;
myStruct init(int x){
  myStruct s;
  s.field = &x;
  return s;
}

然后我声明其中两个结构,然后打印第一个结构的字段。

int main(){
  myStruct s1 = init(1);
  myStruct s2 = init(2);
  printf("s1.field=%d\n",*s1.field);
  return 0;
}

其结果是“ s1.field = 2”。为什么第二个的初始化会影响第一个?

3 个答案:

答案 0 :(得分:2)

您两次调用此函数:

myStruct init(int x){
  myStruct s;
  s.field = &x;
  return s;
}

在两个调用中,都分配了变量(或参数)x,但在函数返回时不再有效。您已经存储了本地变量的地址,并且由于使用的是相同的函数,因此本地变量地址第二次相同,这说明了“魔术”(仍然是未定义的行为,但大多数使用堆栈作为自动变量的编译器将产生相同的结果)

不幸的是,编译器不够聪明,无法检测到您正在存储局部变量的地址。当您返回本地地址(通常更容易检测)时,它们通常会触发警告。

例如,如果您调用printf,将不会得到1或2,而是会产生完全的垃圾,因为参数存储器的组织方式与您的函数不同。

一种干净的方法是分配动态内存:

s.field = malloc(sizeof(*s.field));  // this memory persists even after the end of "init"
*s.field = x;  // copying the value, not the address

当然,当不使用结构时,它需要释放。

答案 1 :(得分:1)

任何变量在内存中都有一些空间。指针引用该空间。当函数调用返回时,局部变量所占用的空间将被释放,这意味着它可以并且将被其他东西重用。因此,对该空间的引用将指向完全不相关的事物。

首选方法是使用malloc()保留非本地内存。这里的危险是您必须释放(free())使用malloc()分配的所有内容,并且如果忘记了,则会造成内存泄漏。

答案 2 :(得分:1)

在功能中

myStruct init(int x){
  myStruct s;
  s.field = &x;
  return s;
}

您正在将x的地址分配给s.field。问题是x对于函数来说是本地的,一旦函数退出,就会停止存在(从逻辑上讲-显然,它所占据的内存位置仍然存在,但是现在可用供其他使用,可能会被覆盖)。该指针值现在为无效,尝试取消引用它会导致未定义的行为。

在这种特殊情况下,最有可能发生的事情是 x调用中用于init(1)的空间正在{{1}中被重用和覆盖}调用,并且奇迹般地,该位置要么未被init(2)调用覆盖,要么printf正在向该位置写入printf作为其操作的一部分。