如何编写一个函数来获取任何节点类型的链表并释放它使用的内存?

时间:2009-07-30 13:11:26

标签: c data-structures linked-list

我相信你们中的一些人已经体验过它。我有两个不同类型的链表,我有两个不同的函数,可以释放它们使用的内存。除了一件事之外,这两个功能是相同的。

通常,释放列表的函数如下所示:

void free_list(T *p)
{
    T *next;    /* here */
    while (p != NULL) {
        next = p->next;
        free(p);
        p = next;
    }
}

其中T是节点类型。这些功能之间的唯一区别是标记线 有没有办法编写一个函数/宏来获取指向任何链表的头部的指针并释放它?

我尝试了几个想法,但是我会把它们从你那里剔除,因为它们错了,失败了,不会给细节带来负担。

7 个答案:

答案 0 :(得分:3)

您可以创建类似

的结构
struct my_item
{
    void * item; // put pointer to your generic item here
    struct my_item * next; // NULL = end of list
};

然后有功能

void free_my_list (struct my_item * first)
{
    struct my_item * cur = first;
    while (cur != NULL)
    {
         cur = cur->next;
         free(cur->item);
         free(cur);
    }
}

答案 1 :(得分:3)

考虑到'C'的限制,我的第一个镜头就像一个像

这样的宏
#define FREE_LIST(T, p)\
do {\
    T *next;\
    while (p != NULL) {\
        next = p->next;\
        free(p);\
        p = next;\
    }\
} while(0)

即。你在1990年左右(模板之前)编写C ++通用代码的方式。


(编辑)历史记录 - 对于病态好奇,Dewhurst & Stark Programming in C++试图成为C ++的一种K& R,详细介绍了如何使用宏来模拟(当时仍然是猜测的)写作)我们现在享受的模板行为。大多数原则将自然地反向移植到'C'。

答案 2 :(得分:1)

如果所有的Node结构都以'next'指针作为第一个“成员”声明,则这样:

struct _Node{
    struct _Node *next;
    /* more data */
};

然后你可以有这样的功能

void free_list(void *p)
{
    void *next;
    while (p != NULL) {
        /*getting the content of the first field, assuming that
          sizeof(void*)==sizeof(unsigned long)*/
        next = (void*)*((unsigned long*)p); 
        free(p);
        p = next;
    }
}

通过这种方式,您可以为遵循此模式的任何列表调用free_list(head_of_list)

答案 3 :(得分:1)

只要所有节点类型都以下一个指针开头,您就可以创建一个通用的释放函数,如下所示:

struct generic_node {
    struct generic_node *next;
};

void free_list(void *head)
{
    struct generic_node *p = head, *next;
    while (p != NULL) {
        next = p->next;
        free(p);
        p = next;
    }
}

struct t_node {
    struct t_node *next;

    /* members of thing t */
};

struct q_node {
    struct q_node *next;

    /* different members of thing q */
};

/* Can happily pass t_node or q_node lists to free_list() */

答案 4 :(得分:1)

我想建议一种不同的方法。不是为不同类型的数据创建多个列表类型,而是使用转换或宏体操将相同的算法应用于它们,而是创建一个通用列表类型,将类型特定的行为委托给不同的函数,并将这些函数附加到具有函数的列表类型指针。例如:

struct generic_node {
  void                *data;
  struct generic_node *next;
};
struct generic_list {
  struct generic_node head;
  int   (*cmp)(void * const a, void * const b);
  void *(*cpy)(void * const);
  void  (*del)(void *);
};

cmp指向一个函数,如果* a< * b,如果* a == * b,则为0;如果* a>,则为1; * b,其中a和b已从void *转换为正确的指针类型。例如,

int compareInts(void * const a, void * const b)
{
  int * const la = a;
  int * const lb = b;
  if (*a < *b) return -1;
  if (*a == *b) return 0;
  if (*a > *b) return 1;
}

int compareMyStruct(void * const a, void * const b)
{
  struct myStruct * const la = a;
  struct myStruct * const lb = b;

  if (la->foo < lb->foo && strcmp(la->bar,lb->bar) < 0 && ...) return -1;
  if (la->foo == lb->foo && strcmp(la->bar,lb->bar) == 0 && ...) return 0;
  if (la->foo > lb->foo && strcmp(la->bar, lb->bar) > 0 && ...) return 1;
}

cpy指向一个函数,该函数生成输入参数的深层副本:

void *copyInt(void * const data)
{
  int *theCopy = malloc(sizeof *theCopy);
  *theCopy = *((int *) data);
  return theCopy;
}

void *copyMyStruct(void * const data)
{
  struct myStruct * const lData = data;
  struct myStruct *newStruct = malloc(sizeof *newStruct);
  newStruct->foo = lData->foo;
  newStruct->bar = malloc(strlen(lData->bar) + 1);
  strcpy(newStruct->bar, lData->bar);
  ...
  return newStruct;
}

最后,del指向一个解除分配数据项的函数:

void delInt(void * data)
{
  free(data);
}

void delMyStruct(void * data)
{
  struct myStruct * lData = data;
  free(lData->bar);
  ...
  free(lData);
}

现在,您的列表算法不必担心特定于类型的行为;他们只是通过函数指针调用适当的函数:

void listAdd(struct generic_list * const theList, void * const data)
{
  struct generic_node *cur = &(theList->head);
  struct generic_node *entry = malloc(sizeof *entry);
  entry->data = theList->cpy(data);
  while (cur->next != NULL && theList->cmp(cur->next->data, entry->data) < 0)
    cur = cur->next;
  entry->next = cur->next;
  cur->next = entry;
}
/** */
void listClear(struct generic_list * const theList)
{
  struct generic_node *cur = theList->head.next;
  while (cur != NULL)
  {
    struct generic_node *entry = cur;
    cur = cur->next;
    theList->del(entry->data);
    free(entry);
  }
}

答案 5 :(得分:0)

您想要的正是C ++模板提供的内容。在C语言中,你会遇到使用void指针和宏做错误的事情。

答案 6 :(得分:0)

如果您使用链接列表,请相信我您想使用托管内存。使用链表的有效方法是结构共享 - 随着程序变得越来越复杂,最终将有数百万个共享和部分共享列表。列表将被构建,分享许多其他列表的一部分......它很混乱。

Lisp早期开发了使用垃圾收集来处理这个问题,现在你可以在C / C#/ Java环境中获取它为什么要回去?

如果你真的不能那么你可以通过在你的指针周围放置引用计数包装器来做一个穷人的垃圾收集。查找“智能指针”。但这比管理记忆要困难得多。