C语言的通用链表
来源:互联网 发布:淘宝发错货怎么理赔 编辑:程序博客网 时间:2024/05/22 02:24
在操作系统编程中, 往往是使用C语言, 但C使用起来极为痛苦, 不像C++有方便的STL模板库使用。
linux内核中,有一套非常神奇的通用链表结构,能够方便的使用,管理各种类型的数据,我们今天就来研究一下,内核中的C数据结构。
本文参考:【深入分析 Linux 内核链表】
首先,我们的目标是构建一个循环双链表结构,为何是双链表,还要循环,当然是从易用性考虑了,双链表能够方便的得知自己的上一个元素,在内核中管理数据更为方便。
其一般结构大概是这样:
实现机制
首先定义一个list_node结构,用来保存链表节点信息:
/** * @brief 链表节点结构 */ typedef struct _list_node { struct _list_node *next; struct _list_node *prev; } list_node;
然后用一个存放数据的结构,比如我们要用int型的链表,如下定义:
/** * @brief 数据存放结构 */ typedef struct _Int_List { list_node node; int data; } Int_List;
这和我们传统的链表实现思路好像不大一样,我们经典的C语言链表当然是在链表中保存数据:
/** * @brief 链表节点结构 */ typedef ElementType int; typedef struct _list_node { struct _list_node *next; struct _list_node *prev; ElementType data; } list_node;
但这样的实现必然会造成链表结构被反复定义,难以实现泛型。另外一种思路可能是将ElementType实现成void*指针,然后在使用时进行强制类型转换,但这样必然会带来反复的类型转换,而且其使用相对不便。
这时要出问题了,如果数据在外层,链表在里层,那么如何从链表找到数据呢?
offsetof宏和container_of宏
为了实现从内层元素找外层元素的功能,我们需要用到这两个系统宏,一眼看上去很复杂,但实际上并不难理解。
/** * @brief 确定当前成员变量,在结构体中偏移量的宏 */#ifndef offsetof#define offsetof(type, member) ((size_t) &((type*)0)->member)#endif
/** * @brief 根据成员变量,找包含他的结构体指针 */#ifndef container_of#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})#endif
这两个宏较为复杂,首先是offsetof宏,这个宏用0号指针去寻找成员变量,我们试想,如何是普通的一个结构体,C编译器如何查找其一个成员呢?
例如:
/** * @brief 数据存放结构 */ typedef struct _Int_List { list_node node; int data; } Int_List;
假设我们用Int_List A
语句,实例化一个结构体,然后取到了A的地址0x00001000
那么node的起始地址是不是也是0x00001000
data的起始地址是不是0x00001000+data的偏移量?
那么如何我想求data的偏移量,不就是如下的代码么:
(&A)->data - (&A);
如果A的地址为0时,那么也就不用减了,就是我们宏定义中的用法。
而container_of宏也是采用类似的思路,既然找到了当前list_node成员的偏移量,那么减去偏移量,便是外层包围着的结构体的起始地址。
那好,这样我们就能实现这个链表了,但其实也不用这样费事,还有简单的办法,那就是让我们的被包含的list_node成员结构,处于我们数据存储结构的第一个位置,那么其地址就是包围其结构的地址,直接类型转换即可。
这种思路实际上也被用于C语言的继承机制的实现。
Github项目
到此,我们已经讲解了通用链表的实现思路,大家可以尝试自己实现一个独特的通用链表,具体内容就不一一赘述,我将完整的工程已经发布到了Github上面,欢迎大家前来fork测试。
【Clist项目地址】
本人才疏学浅,如有疏漏,还望指正。
- C语言的通用链表
- 通用双向链表的C语言实现
- C语言通用数据类型链表的实现
- 双向循环链表 C语言通用编程的思考
- C语言通用双向链表的实现
- 实现通用的双向链表(c语言实现)
- c语言实现通用数据结构(一):通用链表
- c语言实现通用数据结构(一):通用链表
- 用C语言实现通用链表
- C语言通用链表实现
- 纯c语言通用链表
- c语言数据结构之通用链表
- C语言通用双向链表的实现--设计的艺术
- 关于C语言通用双向链表实现的测试--回调函数的作用
- C语言实现通用链表初步(一)
- C语言实现通用链表初步(二)
- C语言实现通用链表初步(三)----单元测试
- 一个通用链表模块(C语言)
- 自学历程
- Android编译服务器的管理之二:管理用户
- 第一章第十二题
- 剑指offer--复杂链表的复制
- R1 Lecture 06 Class Notes
- C语言的通用链表
- 选择HttpHandler还是HttpModule?
- eclipse快捷键
- Java-学习计划和需要阅读的书籍
- Lex匹配unicode字符
- 第13题
- 第一章第九题
- C++找对象的季节——常成员函数(点和对称点)
- IBM SPSS 软件家族预测分析模型的商业应用初探,第 1 部分: 应用关联规则模型提高超市销量