为啥释放内存会导致分段错误?

Posted

技术标签:

【中文标题】为啥释放内存会导致分段错误?【英文标题】:Why does freeing the memory lead to segmentation fault?为什么释放内存会导致分段错误? 【发布时间】:2020-03-14 17:52:01 【问题描述】:

我很绝望,因为这段代码时常给我一个分段错误,我不知道为什么。实际上它只是应该添加一些链表注释,打印它们然后通过释放内存来清空列表。

struct int_list 
   int value;
   struct int_list *next;
;
typedef struct int_list IntList;


void list_print(IntList *start)

   IntList *cur = start;
   while(cur != NULL)
   
      printf("%d\n", cur->value);
      cur = cur->next;
   



void list_append(IntList **start, int newval)

   IntList *newel = malloc(sizeof(IntList));
   newel->value = newval;
   newel->next = NULL;

   if(*start == NULL)
   
      *start = newel;
   

   else
   
      IntList *cur = *start;
      while(cur->next != NULL)
      
          cur = cur->next;
      

      cur->next = newel;
   




void list_free(IntList *start)

   IntList *prev = start;                           // prev = start
   while (start != NULL)                            // if start != Null
   
       start = start->next;                         // make start point to the next element
       printf("Deleting %d\n", prev->value);
       free(prev);                                  // delete the previous element
       prev = start;                                // make previous point to start again
   
   printf("\n");



int main(int argc, char *argv[])

   // fill the list
   IntList *start = NULL;
   list_append(&start, 42);
   list_append(&start, 30);
   list_append(&start, 16);

   // print the list
   printf("\nList 1\n");
   list_print(start);
   printf("\n");

   // free the memory and print again
   list_free(start);
   printf("Empty list:\n");
   list_print(start);
   printf("\n");


在我尝试实现 list_free() 之前,一切正常。所以我强烈假设可以在这个函数中找到错误。只需发布其余代码,因为我是结构的新手,并且不能 100% 确定正确处理它们。你知道我做错了什么吗?...

【问题讨论】:

提示:void list_print(IntList *start) IntList *cur = start; ... 可以写成void list_print(IntList *cur) ... 调用list_free()后,start是一个悬空引用。因此,list_print() 的以下调用具有未定义的行为。 【参考方案1】:

指针仍然指向释放内存的内存位置,这也是分段错误的一个实例。 这是“未定义的行为”,并且可能导致发生任意不可预知的事情,因为指向位置的内容是未知的,运行时间不同。

【讨论】:

【参考方案2】:

函数list_free 按值获取其参数。因此该函数处理指向节点的原始指针的副本。因此,指向节点 start 的原始指针保持不变。

因此调用函数list_free后的列表输出

list_free(start);
printf("Empty list:\n");
list_print(start);

具有未定义的行为。

该函数应该像函数list_append那样通过引用接受指向节点的原始指针。

例如

void list_free( IntList **start )

    while ( *start != NULL )
    
        IntList *prev = *start;                     // prev = start
        *start = ( *start )->next;                  // make start point to the next element
        printf("Deleting %d\n", prev->value);
        free(prev);                                  // delete the previous element
    

    printf("\n");

像这样调用函数

list_free( &start );

退出函数后,原始指针start 将等于NULL。那就是列表确实会被释放。

这比列表的客户自己显式设置指向NULL 的指针要好。他可能会犯与您忘记将指针设置为 NULL 时相同的错误。

【讨论】:

【参考方案3】:

由于悬空指针,您有未定义的行为

   list_free(start);

也就是说,start 仍然指向您尝试访问的已释放内存。

您需要在freeing 之后将start 设置为NULL

   list_free(start);
   start = NULL;
   printf("Empty list:\n");
   list_print(start);

【讨论】:

以上是关于为啥释放内存会导致分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

C ++释放共享库中动态分配的内存导致崩溃

为啥我的自动释放对象没有被释放?

为啥有时会立即释放内存,而有时仅在自动释放池耗尽时才释放内存?

为啥反复分配和释放内存会耗尽系统所有内存?

释放的分配内存仍然可以访问[重复]

ios:多次使用相同的sqlite参数会导致过早的内存释放