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

1 篇文章 0 订阅
1 篇文章 0 订阅

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


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


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


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


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


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


想到就开始做吧。


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

#define MAX_LIST	3

简单起见,我们定一个结构体来虚拟需要存放到链表中的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
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

廖某

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值