【问题标题】:Still able to access a struct that has been freed [duplicate]仍然能够访问已被释放的结构 [重复]
【发布时间】:2014-05-28 19:14:47
【问题描述】:

所以,我有一个结构:

tyepdef struct node {
    struct node *next;
    double value;
} NodeT, *NodeTP;

我有三个功能:

int deleteNode(NodeTP p)
{
    free(p);
    p = NULL;

    return 1;
}

int deleteOne(NodeTP list)
{
    if (list->next != NULL)
        deleteOne(list->next);
    else 
        deleteNode(list);

    return 1;
}

int print(NodeT list)                                                           
{                                                                               
    printf("%lf\n", list.value);                                                

    if (list.next != NULL)                                                      
        print(*list.next);                                                      

    return 1;                                                                   
}   

deleteOne 会将列表中的最后一个节点交给 deleteNode,以便释放内存。初始化节点时,不会为下一个节点分配内存,直到需要它为止。相反,它最初设置为 NULL。这就是我不释放的原因(p->next)。

所以,如果我有一个节点列表(假设它们的值为 3.5、2.5、1.2、3.6) 打印功能将打印以下内容:

3.5
2.5
1.2
3.6

那么,如果最后一个节点被删除了,我再打印一次,会打印如下:

3.5
2.5
1.2
3.6

如果我删除另一个节点,我会收到错误消息,因为我正在尝试释放已释放的内存。

似乎 print 仍在访问一个已被释放的内存位置。但是,如果“下一个”节点等于 NULL,它似乎不应该尝试读取它。 我意识到这是未定义的行为,但我该如何解决这个问题?

【问题讨论】:

  • 我看不到你的调用代码,但 deleteNode() 是否应该接受指向 p 的指针?
  • 未定义的行为是未定义的行为。这不是“保证崩溃”或“保证垃圾值”。解决方案:如果你需要访问一些内存,那就不要释放它。
  • p = NULL; 在您的 deleteNode 函数中无效。您所做的只是修改指针的 local 副本。因此,当您调用deleteNode(list);(在此处添加缺少的分号)时,您实际上并没有更改list 的值。对deleteNode 的调用将free list指向的内容,但list 通过值传递给deleteNode,因此保持不变。

标签: c struct free c99


【解决方案1】:

当链表的tail node被删除时,程序员有责任将前一个节点的next元素设置为NULL。

tail nodefree()ed 时,节点占用的内存将继续保存分配给它的值(当它是一个节点时),直到该内存用于其他目的。因此,释放后继续引用该内存不是一个好主意。

有时,我编写自己的free() 函数来确保传递给free 的指针不再能够访问内存。像这样的:

void myFree(void **ptr)
   {
   free(*ptr);
   *ptr=NULL;
   }

那么,当尾节点被释放时,父节点的next指针自动设置为NULL:

void myFreeTailNode(NodeT **head)
   {
   NodeT *parentNode   = NULL;

   if(NULL == head)
      return;

   if(NULL == *head)
      return;

   if(NULL == (*head)->next)
      myFree((void **)head);
   else
      {
      for(parentNode = *head; parentNode->next->next; parentNode=parentNode->next)
          /*Do Nothing */ ;

      MyFree((void **)&parentNode->next);
      }
   }

【讨论】:

    【解决方案2】:

    如果你想在函数外部分配 NULL,你应该这样做:

    int deleteNode(NodeTP * p)
    {
        free(*p);
        *p = NULL;
    
        return 1;
    }
    

    并更改您对 deleteNode 的调用。

    【讨论】:

    • 遗憾的是,这似乎根本不会影响代码的执行方式
    • 如果要在该函数之外修改列表,则需要对 deleteOne 应用相同的更改。
    • 也许我不明白,但是... NodeTP 和 NodeT * 一样,所以这个改变不应该没有区别吗?另外,如果我对 deleteOne 进行此更改,这不会破坏我的 if 语句吗?因为,我将无法再访问 list->next?
    • 根据经验,如果要修改参数的值(而不是它指向的内存),则必须改为传递指向该参数的指针。您想修改参数“列表”,因此列表已经是指针并不重要:您必须将指针传递给它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 2011-02-26
    • 2012-07-02
    • 1970-01-01
    • 2015-10-07
    • 2020-12-28
    相关资源
    最近更新 更多