簡體   English   中英

在C中釋放一個雙向鏈接列表

[英]Free a doubly linked list in C

我在C語言中有一個雙鏈表,我對如何釋放它感到困惑。 我了解我必須遍歷列表以釋放每個節點。 混亂之處在於每個節點都有一個指向其他數據的指針,我不確定應該如何釋放它。

我的雙向鏈接列表如下所示:

typedef struct Node_ Node;
typedef struct List_ List;

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

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

為了釋放列表,我創建了一個名為List_free()的函數,該函數遍歷列表以使用Node_free()釋放每個節點。 這些函數如下所示:

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);
}

下降的地方是node-> data指向另一個本身包含指針的結構的指針。 就我而言,我使用相同的列表代碼來存儲兩個不同的結構。

我看到的方式有以下幾種選擇:

  1. 創建列表,其中節點保存特定數據。 不是很可重用。
  2. 尋找另一種方式來跟蹤節點數據中的指針。

我是沿着正確的方向思考還是錯過了明顯的事情? 這是我第一次嘗試C語言,因此,如果這完全是錯誤的,我也不會感到驚訝。

一種解決方案是提供負責正確釋放節點的功能指針。

typedef void(*NodeDataFreeFn)(void*);

List_free的修改如下:

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);
}

示例data_free:

void data_free_fn(void* data_ptr) {
    // Add your custom stuff here.
    free(data_ptr);
}

對List_free的示例調用:

List_free(my_list, data_free_fn);

如果您不想按參數傳遞數據自由函數指針,則可以將其存儲在List結構中,如下所示:

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

免責聲明:我沒有測試此代碼,可能有錯誤...

沒錯,這是OOP(尤其是C ++)“解決”的問題之一。

您可以在結構中添加指向函數成員的指針,該指針將用於釋放其數據。 基本上是手動添加一個C ++析構函數。 這樣可以保持通用性,而無需過多重復。

如果應用程序可以在您的列表中存儲任意數據,那么應用程序還有責任安排適當的清理。
這可以通過兩種方式完成:

  1. 在調用List_free之前,應用程序必須清除列表中的所有元素。
  2. 您將另一個參數添加到List_free (和Node_free )中,該參數是刪除函數的指針:

     typedef void (deleter_t*)(void*); 

    \n\n

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

    對於不必顯式釋放的數據,應用程序可以傳遞一個NULL刪除器,對於可以通過單個free調用釋放的數據,可以將該函數作為刪除器傳遞。 對於更復雜的數據,應用程序必須傳遞執行正確操作的函數。

您的問題不在問題所指示的雙向鏈接列表中,而是在復合動態結構中。 這就是手動內存管理的工作方式:您必須跟蹤已分配的內容。 最好每次都堅持堅持節點->數據具有相同的結構,以便預定義的例程可以釋放它。 或者,在結構上使用標記,然后在運行時打開標記,以在針對不同結構的釋放例程之間進行選擇(或僅使用函數指針來模擬C ++析構函數)。

嗯:創建一個Data_free函數並為您的數據指針調用它。

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);
}

編輯:更改struct Node_定義,使其更類似於C ++類:-)

struct Node_ {
    void *data;
    void (*freedata)(void*);
    struct Node_ *next;
    struct Node_ *prev;
}

您可能想嘗試的一種方法是實現內存池。 如果您不打算單獨釋放節點,則實現起來特別容易。

只需簡單地malloc (或mmap ,可能是/dev/zero )一個大塊,然后可以通過簡單的指針添加為一個節點“分配”內存。 這樣,取消分配整個列表就可以free (或單munmap )大塊數據。

如果做得正確,這也可能會稍微提高性能。

就像其他人所說的那樣,您已經確定了在C語言中手動完成的工作很麻煩,但是必須完成。 您可以使用函數指針使釋放機制與您的數據通用,但仍必須實現該機制以使每個節點中的數據層次結構下降以釋放所有內容。

這是一個很好的練習,值得為學習經驗而做。

但是,還有一個非常有用的庫可以為您處理所有這些事情: talloc 它會為您跟蹤層次結構,因此釋放頂層會自動釋放下面的所有內容。 有據可查。 它是為桑巴開發的,至今仍在使用,因此維護得很好。

問題不在於名單有prev指針。 問題在於,每種不同類型的Node都有不同的刪除其內容的方式。

因此,需要一個通用的Node_free例程(您已擁有),並且需要根據節點的類型進行分派。 因此,該節點需要包含通用類型代碼,或者需要包含指向針對該節點類型量身定制的析構函數例程的指針。

無論哪種方式,您基本上都是在做C ++為您做的事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM