ucore版链表介绍
ucore是清华大学操作系统实验课要完成的操作系统,里面有个链表数据结构我觉得很有意思,记录下来。
ucore将链表与数据对象分离,使得任意数据对象,只要加上一个链表组件就能组织成一个链表。
要使得一个本来不具有“把自己的对象组织成链表”能力的类型变得有能力组织成链表,只需要
- 在定义时多加要给list_entry_t成员.
- 定义一个letoxxx的宏,这个宏负责把链表指针转化为对象指针。
从data指针获取下一个data指针的示例
取邻居节点的步骤有三个
第一:取出当前节点的le。
第二:取出当前le 的下一le的地址。
第三:调用le2data,把le指针转化为data指针。
代码如下
假设头节点的指针式head。
那么第二个节点通过下面步骤获取
list_entry_t le=head->le;
list_entry_t* next_le=le.next;
data* next_data=le2data(next_le);
多说几句
这样看链表的使用变得麻烦了。以前我们使用链表都是head->next()->data()。ucore的链表需要3个步骤。
但是链表的开发变得非常简单。对于任意数据结构,只需要加一个list_entry_t成员,然后定义一个le2xxx宏就可以了。而不用给每个数据结构实现next(),prev(),add(),delete()方法。
也许你会想到c++的链表,要把数据组织成链表只需要一行代码:list<data>,就行了。但是这里用到了模板语法。在c语言中没有模板。
c++的链表是包装,它不修改data本来的结构。
ucore的链表是加工,它在data里面加了一点东西,但增加的东西很少,而且丝毫不影响原来的使用。这是在编译器不支持模板情况下非常好的解决方案。
链表实现
le2data
这里最关键的就是如何把list_entry_t指针le变成data指针。我们知道le是data的一个成员,我们得到le,换个视图看就是这个样子:
如果把le指针向前偏移offset大小,不就是data指针了吗?
所以问题转化为如何获取offset。
下面,需要比较好的c语言基础,如果看不懂,也没关系,因为这些实现可以cv,直接用就好。
定义一个宏:
type就是类型,在本语境中,指data。member指类成员,在本语境中指le。
offsetof(struct data,le)的意思就是计算le成员在data中的偏移量。
为了解读方便,我把所有的type替换为data,把所有的member替换为le。
第二行代码解读如下
(data *)0 :我们知道,指针其实就是一个32位的整形。0是32位的整形,所以把0转为了data的指针是允许的。其含义是:内存0号地址开始的若干单元,存放的是一个data类型的对象(虚拟的,实际上并不存在这个对象),我现在用一个没有名字的data指针指向0号地址。既然他是data指针,就一个用->取它的成员数据。
((data *)0->le),取出那个在0号地址的data对象的le成员。
&((data *)0->le),获得那个le成员的地址。这个地址在数值上=offset。因为le的地址=data地址+offset。那么offset=le的地址-data地址=le的地址-0=le的地址。
现在,得到了offset的数值,但编译器此时认为这个数值是个指针,通过(size_t)&((data *)0->le)强制把上一步的指针转化为整形(size_t就是无符号整形)。
le2data的整体实现
通过3个宏
实际上,我们要实现le2xxx,只需要改一个红色框框住的那部分就行。
链表操作的实现
增加,删除,这些操作就是普通的链表操作,实现也是基本 一致,就不详细展开了。