点击打开链接链表是一种很基本的数据结构,也是一种很常用的数据结构。最近编写了几个小程序,多处用到链表,现在就在这里总结一下自己的思考和经验,也请诸位高手多多指教。
其实理解了结构体和指针之后,链表的建立和使用还是挺简单的,但是按照教科书上写的来用,我们常常会遇到一个问题,就是链表的结构是不可以通用的,要存储不同的数据,就要重写不同的结构,重写构造链表的函数,十分麻烦,那我们可不可以写一个通用的链表呢?当然是可以的,下面我就介绍一下这个通用链表的构建以及实用方法。
闲话不多说,我们首先要建立一个节点的结构体:
typedef struct node
{
//数据域
void* data;
//指针域
struct node* next;
}NODE,* p2NODE;
这个结构体的数据域只有一个void型的指针,void指针在使用的时候灵活性是很大的。
下面我们就看一下如何使用这个节点的结构构件一张链表。
我们在建立链表的时候需要有一个头节点,我们在这里定义一下:
p2NODE head = NULL;
我们这张链表的头节点是需要存储数据的,所以链表长度为0 的时候,head要指向NULL,下面我们就写一个函数,每调用一次这个函数,就会向这个链表新添加一个节点,这样循环调用一下,我们这个链表就可以建立起来了:
p2NODE add_to_list(p2NODE head, void* data, size_t data_size)
{
//创建节点并为节点分配空间
p2NODE new_node = NULL;
new_node = (p2NODE)malloc(sizeof(NODE));
memset(new_node, 0, sizeof(NODE));
//为数据域分配空间并填充数据
new_node->data = malloc(data_size);
memcpy(new_node->data, data, data_size);
//将新节点链接到链表(头插法)
new_node->next = head;
head = new_node;
return head;
}
这个函数需要我们传递三个参数,头节点地址p2NODE head,需要存储的数据的地址缓冲区的地址,需要强制转换成void*型,以及要存储数据的大小,由于采用头插法,每调用一次链表的头节点是要改变的,所以返回新的头节点,这样我们就可以用这个NODE结构体和函数构造我们的链表了,下面我们就写一个简单的测试函数,看看这张链表的使用方法:
我们建立一张长度为5 的链表,每一个节点存储一个字符型数据,由输入获得
结构体定义和函数声明我们可以写在linklist.h 这个头文件中
#include "linklist.h"
int main(int argc, char* argv[])
{
char c;
p2NODE head = NULL;
p2NODE p = NULL;
int i = 0;
for(i=0;i<5;i++)
{
printf("请输入字符:");
fflush(stdout);
scanf("%c", &c);
head = add_to_list(head, (void*)&c, sizeof(char));
}
p = head;
while(p != NULL)
{
printf("%c\n", *(char*)(p->data)); //注意这里,p->data是一个void*,我们必须把它转换为相应类型的指针再取值
p = p->next;
}
return 0;
}
通过这样一个简单的测试函数,这张链表的使用方法已经很明了了吧,这里的数据缓冲区char c可以替换为其他的数据类型以及自己定义的结构体,只要建立链表时传入相应的sizeof()就行,当然上面的测试函数还有一个很大的错误,就是没有释放链表空间,这样我们需要些一个释放函数:
p2NODE free_list(p2NODE head)
{
p2NODE p = NULL;
while(head != NULL)
{
p = head->next;
free(head->data);
free(head);
head = p;
}
return head;
}
传入head释放链表,返回最后的head是为了将释放掉的链表的头节点指向NULL;
我写的linklist.c linklist.h的源代码我已经上传,下面是连接地址,有图方便的同学下载下来包含以下就可以直接用了