C 仍然能够访问已释放的结构

C 仍然能够访问已释放的结构,c,struct,free,c99,C,Struct,Free,C99,因此,我有一个结构: 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

因此,我有一个结构:

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仍在访问已释放的内存位置。但是,如果“next”节点等于NULL,则它似乎不应该尝试读取此值。
我知道这是一种未定义的行为,但如何解决此问题?

如果希望在函数外部分配NULL,则应执行此操作:

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

    return 1;
}

并更改对deleteNode的调用。

当链表的
尾部节点
被删除时,程序员有责任将上一个节点的
下一个
元素设置为NULL


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

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

void myFree(void **ptr)
   {
   free(*ptr);
   *ptr=NULL;
   }
然后,当尾部节点被释放时,父节点的下一个指针将自动设置为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);
      }
   }

我看不到您的调用代码,但deleteNode()是否应该接受指向p的指针?未定义的行为就是未定义的行为。这不是“保证崩溃”或“保证垃圾值”。解决方法:如果你需要访问一些内存,那么不要释放它deleteNode函数中的code>无效。您所做的只是修改指针的本地副本。因此,当您调用
deleteNode(列表)时
(在此处添加缺少的分号),实际上并没有更改
列表的值。调用
deleteNode
释放
列表所指向的内容,但是
list
通过值传递给
deleteNode
,因此保持不变。遗憾的是,这似乎根本不会影响代码的执行。如果您希望在该函数之外修改列表,您需要对deleteOne应用相同的更改。也许我不理解,但是。。。NodeTP和NodeT*是一样的,所以这个改变不应该没有区别吗?另外,如果我将此更改为deleteOne,这不会破坏我的if语句吗?因为,根据经验,我将无法再访问list->next?,如果要修改参数的值(而不是它指向的内存),则必须传递指向该参数的指针。您希望修改参数“list”,这样列表已经是指针就无关紧要了:您必须向它传递一个指针。