在C语言中释放一个双链表

在C语言中释放一个双链表,c,memory,free,C,Memory,Free,我有一个C语言的双链表,我不知道该如何释放它。我知道我必须遍历列表来释放每个节点。混淆之处在于,我的每个节点都有一个指向其他数据的指针,我不确定该如何释放这些数据 我的双链接列表如下所示: typedef struct Node_ Node; typedef struct List_ List; struct Node_ { void *data; Node *next; Node *prev; }; struct List_ { Node *firstNod

我有一个C语言的双链表,我不知道该如何释放它。我知道我必须遍历列表来释放每个节点。混淆之处在于,我的每个节点都有一个指向其他数据的指针,我不确定该如何释放这些数据

我的双链接列表如下所示:

typedef struct Node_ Node;
typedef struct List_ List;

struct Node_ {
    void *data;
    Node *next;
    Node *prev;
};

struct List_ {
    Node *firstNode;
    Node *lastNode;
};
void *List_free(List *list)
{
    Node *next = list->firstNode;

    while(next)
    {
        Node *node = next;
        next = node->next;
        Node_free(node);
    }

    free(list);
}

void Node_free(Node *node)
{
    free(node->data);
    free(node);
}
void *List_free(List *list, NodeDataFreeFn data_free)
{
    Node *next = list->firstNode;

    while(next)
    {
        Node *node = next;
        next = node->next;
        (*data_free)(node->data);
        free(node);
    }
    free(list);
}
为了释放列表,我创建了一个名为list_free()的函数,该函数遍历列表,使用node_free()释放每个节点。这些函数如下所示:

typedef struct Node_ Node;
typedef struct List_ List;

struct Node_ {
    void *data;
    Node *next;
    Node *prev;
};

struct List_ {
    Node *firstNode;
    Node *lastNode;
};
void *List_free(List *list)
{
    Node *next = list->firstNode;

    while(next)
    {
        Node *node = next;
        next = node->next;
        Node_free(node);
    }

    free(list);
}

void Node_free(Node *node)
{
    free(node->data);
    free(node);
}
void *List_free(List *list, NodeDataFreeFn data_free)
{
    Node *next = list->firstNode;

    while(next)
    {
        Node *node = next;
        next = node->next;
        (*data_free)(node->data);
        free(node);
    }
    free(list);
}
这里要解决的问题是node->data是指向另一个本身包含指针的结构的指针。在我的例子中,我使用相同的列表代码来存储两个不同的结构

在我看来,我有以下选择:

  • 创建节点保存特定数据的列表。不太可重复使用
  • 找到另一种跟踪节点数据中指针的方法

  • 我的思路是正确的还是遗漏了一些明显的东西?这是我在C的第一次尝试,所以如果这完全是错误的,我不会感到惊讶。

    你没有错,这是OOP解决的问题之一,特别是C++。


    您可以在结构中添加指向函数成员的指针,该指针将用于释放其数据。基本上添加了一个C++析构函数。这将保持泛型而不会有太多重复。

    您的问题不在问题所指出的双链接列表中,而是在复合动态结构中。这就是手动内存管理的工作原理:您必须跟踪分配的内存。最好每次都坚持使用相同结构的节点->数据,这样预定义的例程就可以释放它。或者,在结构上使用标记,在运行时打开标记,在不同结构的分配程序之间进行选择(或者只使用函数指针来模拟C++析构函数)。< /P> < P> HMMM:创建<代码> DATAULFEX/Ung>函数,并调用它以获得数据指针。
    void Data_free(void *ptr)
    {
        /* first identify what data type ptr really is */
        /* and then deallocate memory used by ptr */
    }
    
    void Node_free(Node *node)
    {
        /*free(node->data);*/
        Data_free(node->data);
        free(node);
    }
    

    <>编辑:更改<代码>结构NoDEi定义,使其更类似于C++类:-< /P>
    一种解决方案是提供一个函数指针,负责正确释放节点

    typedef void(*NodeDataFreeFn)(void*);
    
    列表\u free修改如下:

    typedef struct Node_ Node;
    typedef struct List_ List;
    
    struct Node_ {
        void *data;
        Node *next;
        Node *prev;
    };
    
    struct List_ {
        Node *firstNode;
        Node *lastNode;
    };
    
    void *List_free(List *list)
    {
        Node *next = list->firstNode;
    
        while(next)
        {
            Node *node = next;
            next = node->next;
            Node_free(node);
        }
    
        free(list);
    }
    
    void Node_free(Node *node)
    {
        free(node->data);
        free(node);
    }
    
    void *List_free(List *list, NodeDataFreeFn data_free)
    {
        Node *next = list->firstNode;
    
        while(next)
        {
            Node *node = next;
            next = node->next;
            (*data_free)(node->data);
            free(node);
        }
        free(list);
    }
    
    免费数据示例:

    void data_free_fn(void* data_ptr) {
        // Add your custom stuff here.
        free(data_ptr);
    }
    
    调用List_free的示例:

    List_free(my_list, data_free_fn);
    
    如果不想通过参数传递无数据函数指针,可以将其存储到列表结构中,如下所示:

    struct List_ {
       Node *firstNode;
       Node *lastNode;
       NodeDataFreeFn data_free;
    };
    

    免责声明:我没有测试此代码,它可能有缺陷…

    如果应用程序可以在您的列表中存储任意数据,则应用程序也有责任安排适当的清理。
    这可以通过两种方式实现:

  • 在调用
    List\u free
    之前,应用程序必须清除列表中的所有元素
  • 将另一个参数添加到
    列表\u free
    (和
    节点\u free
    )中,该参数是指向删除器函数的指针:
    
    
    
    typedef void (deleter_t*)(void*);

    void Node_free(Node* node, deleter_t deleter) { if (deleter) (*deleter)(node->data); free(node); }
    typedef void(deleter_t*)(void*)

    void Node_free(Node*Node,deleter_t deleter) { if(deleter)(*deleter)(节点->数据); 自由(节点); }
    对于不必显式释放的数据,应用程序可以传递NULL deleter;对于可以通过单个
    free
    调用释放的数据,该函数可以作为deleter传递。对于更复杂的数据,应用程序必须传递一个执行正确操作的函数


  • 您可能希望尝试的一种方法是实现一个内存池;在您不打算单独释放节点的情况下,实现这一点可能特别简单

    只需
    malloc
    (或者
    mmap
    ,可能是
    /dev/zero
    )一个大的块,然后您就可以通过简单的指针添加为一个节点“分配”内存。然后,取消分配整个列表就是
    free
    ing(或
    munmap
    ing)大块的问题


    如果做得正确,这也可能会稍微提高性能。

    正如其他人所说,您已经发现在C中手动执行某些操作是一件很痛苦的事情,但必须执行。您可以使用函数指针使释放机制通用于您的数据,但您仍然必须实现该机制,以向下遍历每个节点中的数据层次结构,从而释放所有数据

    这是一个很好的练习,值得为学习体验而做


    但是还有一个非常有用的库,可以为您处理所有这些问题:。它为您跟踪层次结构,因此释放顶层会自动释放底层的所有内容。这是有据可查的。它是为samba开发的,现在仍在使用,因此维护得非常好。

    问题不是列表中有
    prev
    指针。问题在于,每种不同类型的
    节点都有不同的删除其内容的方法

    因此需要有一个通用的
    节点_free
    例程(您已经有了),它需要根据节点的类型进行调度。因此,要么节点需要包含泛型类型代码,要么它需要包含指向为该节点类型定制的析构函数例程的指针


    无论哪种方式,你基本上都在做C++所做的事情。< / P > Hmm.。我会使用C++并依赖析构函数。祝你在C++中编写你的操作系统或内核驱动程序或嵌入式应用程序;Alexander Rafferty:如果你打算改变语言来避免这个问题,那么就换一种带有垃圾收集器的语言,例如Java。为什么不改成Lisp呢?它对列表有很好的支持。如果数据是空的,我可以使用列表中的任何类型的数据。这是我的问题。我对不同的数据类型使用相同的列表代码。如果我为我打算存储的每个数据类型创建单独的列表,我可以按照你的建议去做。你肯定有办法确定你的void指针的数据类型,对吗?只需在
    免费数据中使用它,但它不再是可重用的代码。每次添加新的数据类型时,都必须更改数据类型。我能想到的唯一其他方法是更改
    struct节点