【问题标题】:weird issue using struct pointer in c在c中使用结构指针的奇怪问题
【发布时间】:2013-02-04 02:01:58
【问题描述】:

好的,所以我对 c 中的结构完全陌生,我遇到了一个对我来说似乎很奇怪的问题。
当使用它的指针将一个简单的结构传递给一个函数时,该结构会自动采用该函数的其他参数之一,因为它是新数据。我不知道为什么会发生这种情况.. 此时 move_walker() 应该什么都不做,对吧?

typedef struct {
    int x,
        y;
} walker_t;

walker_t* init_walker(int x, int y) {
    walker_t walker;
    walker.x = x;
    walker.y = y;
    walker_t *pointer = malloc(sizeof(walker));
    pointer = &walker;
    return pointer;
}

int move_walker(walker_t * walker, int direction) {
    return 0;
}

walker_t* walker;
walker = init_walker(8,2);

printf("%d %d\n", walker->x, walker->y); //will print '8 2'
move_walker(walker, 3);
printf("%d %d\n", walker->x, walker->y); //will print '0 3'

(我很确定没关系,但这段代码实际上分布在多个文件中。)

【问题讨论】:

  • 你打错了 ;-) 更改指针 = &walker; for *pointer = walker;
  • 您为什么决定为此使用动态内存分配?

标签: c struct pass-by-reference


【解决方案1】:

问题是您的walker 指针指向无效的堆栈内存,因为init_walker 有一个错误:您在堆栈上创建一个walker_t 结构,然后使用malloc 保留内存并分配该地址内存到pointer。到目前为止一切顺利。

但是,pointer = &walker将结构从堆栈复制到新内存,而是使pointer 指向堆栈上的结构! &walkerwalker 的地址,您将其分配给您的指针。您可能想要做的是复制结构。为此,您必须取消引用您的指针:

*pointer = walker

这应该使您的程序按预期工作。您也可以完全跳过堆栈上的结构:

walker_t* init_walker(int x, int y) {
    walker_t *walker = malloc(sizeof(walker_t));
    walker->x = x;
    walker->y = y;
    return walker;
}

【讨论】:

    【解决方案2】:

    您的init_walker 是错误的,因为它返回一个指向堆栈局部变量walker 的指针。一旦init_walker 退出,该变量的内存就会被回收。你的第一个 printf 仍然有效,有点意外,因为你的 walker 变量的值在堆栈上仍然没有改变。但是,一旦您在此之后进行任何函数调用,原始init_walker 调用的堆栈帧就会被覆盖,walker 指针现在指向一些随机垃圾。

    当您在init_walker 中使用malloc 时,您已经在为您的walker_t 在堆上分配内存(与堆栈不同,它的存在时间超出堆栈帧的生命周期)。所以,你应该这样做:

    walker_t* init_walker(int x, int y) {
        walker_t *pointer = malloc(sizeof(walker_t));
        pointer->x = x;
        pointer->y = y;
        return pointer;
    }
    

    【讨论】:

      【解决方案3】:

      您正在堆栈上创建结构对象。您需要使用

      来分配它
      walker_t* init_walker(int x, int y) {
      walker_t* walker = malloc(sizeof(walker_t));
      ...
      return walker;
      }
      

      walker_t *pointer = malloc(sizeof(walker));
      pointer = &walker;
      

      您正在制造内存泄漏!您将新内存分配给 *pointer 并在将 &walker 分配给指针时丢失指针。

      【讨论】:

      • 内存泄漏是另一个问题,您的解决方案无法解决。您要解决的问题是取消引用悬空引用,而不是内存泄漏!
      • 你是对的!这行得通。尽管我仍然不完全理解问题是什么,因为第一个 printf 语句有效,但是一旦将指针传递给函数,它就不会。
      • @icepack 对不起,不明白你的意思...我将内存分配给一个新对象,超出此范围的内容在此功能中并不重要。
      • @user1666419 它起初可以工作,因为您指向的内存仍然具有堆栈分配的walker的值。内存不再有效,但其他任何东西都不需要它,所以它不会被覆盖。但是,一旦您调用该函数,它就会建立自己的堆栈框架,从而覆盖您的结构。
      • @icepack 不完全正确。 init 函数返回一个用户在使用后需要释放的指针是正常的。但是,如果在 malloc 之后直接覆盖指针,您将失去任何释放内存的机会。
      【解决方案4】:
       walker_t* init_walker(int x, int y) {
          walker_t walker;
          walker.x = x;
          walker.y = y;
          walker_t *pointer = malloc(sizeof(walker));
          *pointer = walker;  /* here was the error. Copy the value not the adress */
          return pointer;
      }
      

      但它可以更简单:

      walker_t* init_walker(int x, int y) {
      
          walker_t *pointer = malloc(sizeof(*pointer));
          pointer->x = x;
          pointer->y = y;       
          return pointer;
      }
      

      【讨论】:

        【解决方案5】:

        你的代码是把它说成“非常奇怪”的政体。

        这样会更好...

        walker_t *init_walker (int x, int y)
        {
            walker_t *p_walker = (walker_t *)malloc (sizeof(walker));
        
            if (p_walker != NULL)
            {
                p_walker->x = x;
                p_walker->y = y;
            }
            return (p_walker);
        }
        

        完成后致电free (walker)

        【讨论】:

        • 为什么要转换malloc的结果?我想听听原因。
        • 因为如果你编译代码时启用了很多编译器警告(这是一件非常好的事情),或者编译为 C++,你会收到一个关于将 void* 转换为 walker_t* 的警告\错误.编译器警告是你的朋友,启用它们。
        • 在 C 中,您确实不会在任何体面的编译器上收到任何关于 void 指针隐式转换的编译器警告。例如,尝试删除强制转换并使用gcc -std=c99 -Wall -Wextra -pedantic 进行编译。相反,您的类型转换隐藏 有用的警告,这是危险 on a C90 compiler。还有read this。在 C++ 中,你根本不应该使用 malloc,它是 dangerous in C++
        • 所以请从你的答案中删除类型转换,因为它被标记为 C。
        【解决方案6】:

        ...或者,在对代码进行完整性检查之后,您也可以编写:

        typedef struct 
        {
            int x,
            int y;
        } walker_t;
        
        void init_walker(walker_t* obj, int x, int y) 
        {
           obj->x = x;
           obj->y = y;
        }
        
        walker_t walker;
        init_walker(&walker, 8,2);
        

        【讨论】:

          猜你喜欢
          • 2011-03-09
          • 2014-02-16
          • 1970-01-01
          • 2015-07-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多