代码之家  ›  专栏  ›  技术社区  ›  Mikkel bruun

理解结构中的指针行为

  •  1
  • Mikkel bruun  · 技术社区  · 7 年前

    我定义了一个具有单个字段和init函数的结构,如下所示:

    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 回复  |  直到 7 年前
        1
  •  2
  •   Jean-François Fabre    7 年前

    您将调用此函数两次:

    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
    

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

        2
  •  1
  •   kiran Biradar    7 年前

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

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

        3
  •  1
  •   John Bode    7 年前

    在函数中

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

    您正在分配的地址 x s.field . 问题是 函数的局部函数,函数一旦退出, 不再存在 (逻辑上讲——显然,它占用的内存位置仍然存在,但现在可以供其他东西使用,并且可能会被覆盖)。指针值现在是 无效的 ,尝试取消引用会导致未定义的行为。

    最有可能发生的事情 在这种特殊情况下 是用来 init(1) 调用在中被重用和重写 init(2) 调用,奇迹般的是,该位置不是被 printf 打电话,或 打印 正在编写 2 作为行动的一部分。