【问题标题】:Pop method for doubly linked list implementation in C, do I need to use freeC中双向链表实现的pop方法,是否需要使用free
【发布时间】:2016-07-27 18:55:20
【问题描述】:

作为 C 的初学者,我非常不确定何时应该免费使用。我正在研究一种从列表末尾删除元素的 pop 方法。它还应该返回被移除元素的值。

这是我的 ListListNode 结构

typedef struct ListNode {
    struct ListNode *next;
    struct ListNode *prev;
    void *value;
} ListNode;

typedef struct List {
    int count;
    ListNode *first;
    ListNode *last;
} List;

这是我的 remove_from_back 函数的代码。

void *remove_from_back(List *list)
{
    void *result = NULL;

    if (list->last == NULL) return result;

    result = list->last->value;
    if (list->last->value == list->first->value) {
        list->last = list->first = NULL;    
    } else {
        list->last = list->last->prev;
        list->last->next = NULL;        
    }   
    return result;
}

为节点创建内存的add_to_back函数

void add_to_back(List *list, void *value)
{  
    ListNode *node = calloc(1, sizeof(ListNode));
    node->value = value;

    if (list->first == NULL) {
        list->first = node;
        list->last = node;
    } else {
        list->last->next = node;
        node->prev = list->last;
        list->last = node;
    }

    list->count++;
}

【问题讨论】:

  • 您提到了 C,而 delete 是 C++ 运算符。如果您的节点分配有malloc,则使用free 释放它。一般来说,把它留给分配节点的人来释放它,不要让它成为你函数的一部分。
  • 从不。 c 中不存在删除。也许你的意思是免费的()?
  • 是的,谢谢大家,我更新了问题。 @Jens 你能扩展一下吗?如果我有一个删除节点的函数,为什么我不应该让这个函数释放我要删除的节点?
  • 经验法则:如果调用者分配了内存,他们应该释放内存。如果您的容器在内部分配它,那么它应该释放内存。创建您的函数接口以完全适合这些约定之一,但不要混合使用它们。为了详细说明这一点,我将给出一个来自 C++ 的示例,其中堆栈容器管理自己的内存。为了以一种好的方式处理这个问题,pop 不返回值。它只是将其删除并释放。如果您想要弹出的值,请使用top 之前调用pop
  • @CraigEstey:不一定是泄漏,取决于分配方式。没有足够的背景来做出判断。

标签: c list struct


【解决方案1】:

由于您的add_to_back() 函数为节点分配了内存,remove_from_back() 函数应该删除节点的内存。但是,它不应该释放节点指向的数据。因此:

void *remove_from_back(List *list)
{
    void *result = NULL;

    if (list->last == NULL) return result;

    ListNode *dead = list->last;            // Added
    result = list->last->value;
    if (list->last->value == list->first->value) {
        list->last = list->first = NULL;    
    } else {
        list->last = list->last->prev;
        list->last->next = NULL;        
    } 
    free(dead);                             // Added
    return result;
}

我只添加了两行。我没有更改任何中间行,所以如果原始代码中有错误,那么这些错误也在修改后的代码中。 但是添加的行可以防止数据添加到列表时分配的节点泄漏。

在分配内存时,需要知道对应的free()会发生在哪里。如果您不知道,您需要决定如何释放它并实施释放机制。有时,您可以做出无关紧要的决定——程序无论如何都将退出。但是可重用代码很少能证明这种假设是正确的。这就像打开和关闭文件一样——您可以忽略关闭文件,但最终您会遇到打开文件过多的问题。

【讨论】:

    【解决方案2】:

    通常,pop 方法会从数据结构中删除一个元素并返回一个指向它的指针。然后,您将对元素执行一些操作,例如提取值和/或可能释放分配给元素的内存。在您的示例中,这不是典型的,您很可能会泄漏内存。您失去了对被弹出节点的所有引用。

    如果您需要释放节点,我会修改代码来处理它。

    if (list->last == list->first)
    {
         free (list->last);
         list->last = list->first = NULL; 
    }
    else 
    { 
        ListNode * temp = list->last;
        list->last = list->last->prev; 
        list->last->next = NULL;
        free (temp);
    }
    

    【讨论】:

    • 你能举例说明如何正确释放节点吗?另外,我在哪里取消引用 NULL 值,如果这听起来像一个愚蠢的问题,对不起。
    • 我不再看到取消引用。我猜在某个时候发生了编辑。
    【解决方案3】:

    释放 malloc 是一个很好的做法。如果您使用的是 Linux,请使用 valgrind 检查内存发生了什么。

    我已经在这里发布了我的程序使用 valgrind 的输出。如您所见,我使用了 6 个分配器并释放了 5 个(仅向您展示:))。

    你还可以看到我丢失了 100 个字节,因为我没有释放一个分配。

    现在,想想如果我们不释放我们的分配器会发生什么。

    【讨论】:

      猜你喜欢
      • 2014-07-01
      • 2016-06-10
      • 2015-05-16
      • 2013-10-29
      • 1970-01-01
      • 2015-09-30
      • 1970-01-01
      • 1970-01-01
      • 2021-11-18
      相关资源
      最近更新 更多