关于可控大小的双向循环链表

来源:互联网 发布:微信js获取signature 编辑:程序博客网 时间:2024/06/02 05:03

本人现从事linux嵌入式软件开发工作,入职刚刚1年,是个菜鸟。


最近在工作中遇到了这样一个问题:一个服务器程序接收来自客户端的GPS数据包,服务器解析数据包后,将数据保存到链表中,供给其他接口使用。


问题来了:其他接口可以在调用一条链表中存放的数据后,将这条数据删除,但如果没有及时的读取数据并删除,链表的大小将越来越大,而且大的没有限制,因为服务器仍然在接收新来的数据。


我在这里使用的链表为linux内核中使用的链表,即list.h。大家知道,这个链表非常好用,而且基本上不会出错,但是它不能控制链表的大小。


我起先想到了一个办法,即手动记录链表此时的大小,如果超过一个值的话,就不再把新的数据存放到链表中。但是这个办法显然是不行的,如果链表满了就不存放新数据的话,那我们得到的将是过时的GPS信息,数据将失去意义,而且对于应用层来说,数据将会变的“不正确”。


我能想到的可行的办法,即当链表超过某个大小之后,不再插入新的节点,而是将最新的数据覆盖掉最老的数据,数据就像操场跑圈,在规定大小的链表里奔跑。


想到就开始做吧。


首先定义好链表的大小,这是我们最终的目的:

#define MAX_LIST3

简单起见,我们定一个结构体来虚拟需要存放到链表中的GPS结构体:

typedef struct TEST{int num;struct list_head head;}TEST;TEST *listHead = NULL;

也许你和我一样,想知道今天我们的main函数又要做些什么事情呢?好吧,我们首先来看看main长什么样子:

int main(void){if(MAX_LIST == 0)return (-1);listHead = (TEST *)calloc(sizeof(TEST), 1);InitList(listHead);int i = 1;bool ret = false;//循环创建链表for(; i<10; i++){TEST *newList = NULL;newList = (TEST *)calloc(sizeof(TEST), 1);newList->num = i;ret = InsertList(&(newList->head));if(ret != true)break;}PrintList(listHead);return 0;}

其实长的很简单,首先为头节点分配空间,然后初始化头节点,接着利用循环创建链表,然后打印链表。


需要注意的是,请不要做把链表大小定为0这样逗比的行为,程序会直接无视你的。


接下来我们挨个看看每个函数都做了什么。


首先是初始化函数,完成的事情很简单:

void InitList(TEST *myList){myList->num = 0;INIT_LIST_HEAD(&(myList->head));}

赋初值,然后初始化linux内核链表,这是在list.h中含有的接口。


接着,是我们今天要讲的重点,插入链表接口:

//插入节点static bool InsertList(struct list_head *newer){if(NULL == newer || NULL == listHead)return false;struct list_head *pos = &(listHead->head);if(GetListSize(pos) >= MAX_LIST){TEST *tmp = NULL;tmp = container_of((listHead->head.next), TEST, head);listHead = tmp;DeleteList(pos);}list_add_tail(newer, &(listHead->head));return true;}

函数的参数为需要添加到链表的新节点的地址,如果此时函数的大小没有超过最大值,则正常的调用list.h中的接口,将节点添加到链表中。


但是如果链表此时超过了最大值,则程序分成三步走:

1.将头节点搬家,搬到头节点的下一节点。也就是说,罢免现在头节点的职务,由“二把手”,即头节点的下一个节点担当头节点。

2.删除原头节点。既然老的头节点的职务已经被撤了,肯定是因为摊上了什么事,下面你就要落实金元帅的指示,把老的头节点给突突了。

3.将需要被插入链表的节点插入到新的投机点之前。因为采用的是程序猿界最负盛誉的老汉推车---“尾插法”,所以最新的元素在头节点之前,头节点本身为最老的节点。


关于如何获得链表的大小以及打印链表,大家可以在文章最后给出的源码文件中获悉,这里不做说明,因为很简单,而且我很懒。


按照以上所述的思路,基本上可以完成一个可以控制大小的双向循环链表。如果你想更改链表的限制,只需要更改那个宏定义即可。


最后,给我自己一点思考:

如果现在不用这个结构体了,换另外一个结构体,完成同样的功能,我是不是要重新写一个新的接口,而且代码基本上是一样的。这样代码将无法复用,冗余度相当高。


我查阅了其他资料,可以用C++模板或者其他已有程序的框架来解决,但是鄙人才疏学浅,尚不能知其一二,还是以后再慢慢研究。


关于这篇文章,还有很多细节没有讲到,比如list.h中删除节点的接口,其实没有真正的删除,只是断开了这个节点与链表的联系,也就相当于拐卖了,还没撕票。


有关这些细节,日后再说。


谢谢你用玩撸啊撸、CF等“大型”游戏的时间来阅读本文,希望在促进你肠道消化的同时,对你有所帮助,愿我们共同进步,一起老汉推车。


对了,差点忘记说好的源码了。擦类,都不能上传文件的,大家请移步到资源页。


源码下载地址(含list.h)


0 0