博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
双向链表管理程序
阅读量:4199 次
发布时间:2019-05-26

本文共 5306 字,大约阅读时间需要 17 分钟。

在写一个小工具的时候,需要用到对ini文件的管理。为了让这个小工具在Linux也适用,所以在写程序的时候没有使用mfc提供的类库,也没有使用windows提供的ini操作函数,直接拿标准C写了一个。

在操作ini的过程中,我拿两种双向链表来描述一个ini文件,也就意味着,要针对每种链表都写一套插入、删除之类的操作函数,格外麻烦。索性,就写了一个通用的双向链表管理程序,省的以后麻烦。

这个“通用”的双向链表管理程序的思路是:

每种双向链表的节点结构体里,虽然有太多的不一样,但是next和prev指针肯定是要有的,所以,就可以制定一个只包含next和prev成员的基础双向链表节点结构体类型,如下:

1: struct basic_nod_t {
2:     struct basic_nod_t *prev;
3:     struct basic_nod_t *next;
4: };

这样,只要是具备prev和next指针的节点,都可以强制转换为这种通用型的结构。当然,前提是,一个具体的节点结构体在定义的时候,需要让prev和next做为结构体的第一个和第二个成员,这种强制转换才会有效果。比如,现在有一个用来保存用户信息的节点结构体:

1: struct user_info_t {
2:     struct user_info_t *prev;
3:     struct user_info_t *next;
4:     char *username;
5:     char *passwd;
6: };

因为user_info_t结构体的前两个成员也是prev和next指针,那么,当尝试把user_info_t结构体强制转换为basic_nod_t时,basic_nod_t内的prev和next成员即为user_info_t的对应成员。

这样一来,通用型的管理程序就有机会实现了。因为对链表的插入、删除等操作,在一般情况下,只会牵扯到对prev和next指针的修改,所以,不管具体的结构体长什么样子,只要你有prev和next,我就可以操作!

来个具体的代码吧,拿插入节点为例:

1: void add_node_to(void **head, void **tail, void *node, void *to, int before_or_after)
2: {
3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
4:     struct basic_nod_t **t = (struct basic_nod_t**)tail;
5:     struct basic_nod_t *n = (struct basic_nod_t*)node;
6:     struct basic_nod_t *tt = (struct basic_nod_t*)to;
7:     if(*h == NULL)
8:     {
9:         *h = *t = n;
10:         n->prev = NULL;
11:         n->next = NULL;
12:     }
13:     else if(before_or_after == 0)
14:     {
15:         //move the 'moved' node to the place before 'to'
16:         if(tt == NULL)
17:             tt = *h;
18:         if(*h == tt)
19:             *h = n;
20:         n->prev = tt->prev;
21:         n->next = tt;
22:         tt->prev = n;
23:         if(n->prev != NULL)
24:         {
25:             n->prev->next = n;
26:         }
27:     }
28:     else if(before_or_after == 1)
29:     {
30:         //move the 'moved' node to the place after 'to'
31:         if(tt == NULL)
32:             tt = *t;
33:         if(*t == tt)
34:             *t = n;
35:         n->next = tt->next;
36:         n->prev = tt;
37:         tt->next = n;
38:         if(n->next != NULL)
39:             n->next->prev = n;
40:     }
41: }

函数在传参的时候,采用了void指针类型,因为在实际使用过程中,节点结构体的定义并不一致。

其中,函数的head和tail参数,用来表示链表的头节点和尾节点。因为在插入节点的过程中,可能会修改head和tail,所以,这里在传参的时候,采用的是void**这种指针的指针的形式,用来表示,head和tail这两个指针将来是要被修改的。

在add_node_to函数内部,会首先将传进来的参数转换为basic_nod_t类型,此时,节点内的prev和next指针就可以被操作了。

OK,再来个删除节点的:

1: void del_node(void **head, void **tail, void *node)
2: {
3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
4:     struct basic_nod_t **t = (struct basic_nod_t**)tail;
5:     struct basic_nod_t *n = (struct basic_nod_t*)node;
6:     if(n == *h)
7:     {
8:         *h = n->next;
9:     }
10:     if(n == *t)
11:     {
12:         *t = n->prev;
13:     }
14:     if(n->prev != NULL)
15:         n->prev->next = n->next;
16:     if(n->next != NULL)
17:         n->next->prev = n->prev;
18: }

还有移动节点:

1: void move_node(void **head, void **tail, void *moved, void *to, int before_or_after)
2: {
3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
4:     struct basic_nod_t **t = (struct basic_nod_t**)tail;
5:     struct basic_nod_t *m = (struct basic_nod_t*)moved;
6:     struct basic_nod_t *tt = (struct basic_nod_t*)to;
7:     if(    (h == NULL) || (t == NULL)
8:         || (*h == NULL) || (*t == NULL)
9:         || (m == tt))
10:         return;
11:     del_node(head, tail, moved);
12:     add_node_to(head, tail, moved, to, before_or_after);
13: }

最后再来个链表排线的(这里使用的是最简单的冒泡排序):

1: void sort_list(void **head, void **tail, CMP_FUNC nodcmp)
2: {
3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
4:     struct basic_nod_t *nod1 = *h;
5:     struct basic_nod_t *nod2 = nod1->next;
6:     int swaped = 1;
7:     while(swaped)
8:     {
9:         swaped = 0;
10:         while(1)
11:         {
12:             if((*nodcmp)(nod1, nod2) > 0)
13:             {
14:                 move_node(head, tail, nod1, nod2, 1);
15:                 nod2 = nod1->next;
16:                 swaped = 1;
17:             }
18:             else
19:             {
20:                 nod1 = nod2;
21:                 nod2 = nod2->next;
22:             }
23:             if(nod2 == NULL)
24:             {
25:                 nod1 = *head;
26:                 nod2 = nod1->next;
27:                 break;
28:             }
29:         }
30:     }
31: }

在排序的时候,由于通用管理程序并不知道排序的依据和算法,所以,需要提供给他一个nodcmp函数来完成两个节点大小的计算工作。CMP_FUNC的定义如下:

1: typedef int (*CMP_FUNC)(void *t1, void *t2);

基本搞定,呵呵~~

使用起来也非常简单,仍然拿那个用户信息链表user_info_t为例:

1: // 定义链表的头和尾指针, 初始情况下,链表为空
2: struct user_info_t *head = NULL;
3: struct user_info_t *tail = NULL;
4: // 首先创建一个节点
5: user_info_t *node = (struct user_info_t *)malloc(sizeof(struct user_info_t));
6: // 然后初始化节点的一些必要信息
7: node->username = (char *)malloc(strlen("test") + 1);
8: strcpy(node->username, "test");
9: node->passwd = (char *)malloc(strlen("admin") + 1);
10: strcpy(node->passwd, "admin");
11: // 然后将节点插入链表中
12: add_node(&head, &tail, node);
13: // 同样的方法还可以插入更多的节点;
14: add_node(&head, &tail, node);
15: // 删除节点也同样方便
16: del_node(&head, &tail, node);
17: // 注意,链表管理程序并不会帮助你释放空间,所以,如果节点是在堆中分配的,那么记得在从链表中剔除节点后,将节点占用的空间释放
18: free(node->username);
19: free(node->passwd);
20: free(node);
21: // 搞定

下面是已经写好的管理程序源代码下载链接,这是个VC的工程,包含了一个简单的测试程序,链表管理程序可以参考工程内的queue.c和queue.h,这两个文件可以放到其他平台下编译.

转载地址:http://wxuli.baihongyu.com/

你可能感兴趣的文章
深入Java事务的原理与应用
查看>>
CSS单位和CSS默认值大全
查看>>
交大我来了--周末再见了
查看>>
网页中flash wmode属性
查看>>
挑战自我,勇攀高峰
查看>>
神奇的HTML5画图应用
查看>>
flex 滚动条问题
查看>>
软件开发管理中的博奕论
查看>>
计算机认证考试种类
查看>>
SQL in和exists 比较
查看>>
社会性网络服务(SNS)研究
查看>>
鼠标DarkField技术
查看>>
傻傻的我
查看>>
paypal 沙盒账号注册
查看>>
ebay 沙盒账号注册
查看>>
linux -8 Linux磁盘与文件系统的管理
查看>>
linux -8 Linux磁盘与文件系统的管理
查看>>
linux 9 -文件系统的压缩与打包 -dump
查看>>
PHP在变量前面加&是什么意思?
查看>>
ebay api - GetUserDisputes 函数
查看>>