C语言 如何制作通用链表



我试图在C编程语言和我创建一个通用链表成功了,但我有一个小问题:

linked_list.h

 struct Element {
     void * data;
     struct Element * nEl;
 };
 typedef struct Element Element;
 struct List {
     size_t el_size;
     Element * start_el;
 };
 typedef struct List List;

linked_list.c

 List * create_list(size_t el_size);
 void delete_list(List * ls);
 void append(List * ls, void * data);
 void s_append(List * ls, void * data);

 void append(List * ls, void * data) {
     Element * last_el = ls - > start_el;
     if (last_el == NULL)
         ls - > start_el = last_el = malloc(sizeof(Element));
     else {
         while (last_el - > nEl != NULL)
             last_el = last_el - > nEl;
         last_el - > nEl = malloc(sizeof(Element));
         last_el = last_el - > nEl;
     }
     void * cdata = malloc(ls - > el_size);
     memcpy(cdata, data, ls - > el_size);
     last_el - > data = cdata;
     last_el - > nEl = NULL;
 }

这适用于所有类型,如int, char, float, double等。但它不能与char *一起工作,因为它复制了前4个bytes(实现依赖)string,但不是整个string

问题是列表中每个元素的大小是固定的(el_size)。并不是所有的字符串都有相同的大小,假设每个字符1个字节,"hola"将占用4个字节,而"hi"将占用2个字节。

您应该使用更通用的方法。而不是仅仅分配内存,你应该使用一个函数指针指向你想要保存的对象的构造函数。

在该函数中,您应该正确分配类型所需的空间。使用相同的原则后,不要忘记正确地清理。

这两个函数应该是你的struct的成员。

这可能对你有所帮助:C中的函数指针是如何工作的?

你的列表永远不应该分配和复制数据。这不是它的工作。它只应该存储数据(作为指针)。复制数据是列表创建者的职责。

因此,代替

 void * cdata = malloc(ls - > el_size);
 memcpy(cdata, data, ls - > el_size);
 last_el -> data = cdata;

你只需要

 last_el -> data = data;

也没有释放数据

现在,如果您想要一个拥有其数据的列表,您也可以将其作为基本非拥有列表的包装器。这个想法是你只在需要的时候才这么做。不是每个列表都需要拥有自己的内容。

拥有其内容的列表需要有一种复制和释放数据的方法,这应该以一对函数指针的形式提供,用于复制和释放数据。这并不是一个不必要的负担,因为每种数据类型都应该有这样的函数。

我同意memcpy在链表中是坏主意。我已经在队列中使用它(使用下面的链表)和链表工作,只要我不开始释放(节点->数据)。当我在像front(), pop_front()这样的调用序列中这样做时,为了不丢失我的free()数据,我开始使用memcpy,但这是一个可怕的做法。我想当你通过结构时,它只释放了第一个字段。当你记忆这样的结构((操作泛型void *)时,你只能复制指针,因为它只指向结构的一个字段,所以你失去了这些结构中的所有字段,但不是首先。

那么继续:在INSERT操作中放弃memcpy,同时在删除时放弃free (node->data)

void指针是我自己使用的但如果你想这样做因为你不想为不同的数据类型重写整个列表的实现多次,你可以实现它一次,例如使用

typedef int* DataPtr;

然后,每次你想为不同的数据类型使用列表时,你可以用不同的名字复制源文件和头文件,并用你想要使用的数据类型替换int*。我不是一个超级粉丝,我不会推荐这样做,但在我看来这是值得一提的。

最新更新