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项目地址】

本人才疏学浅,如有疏漏,还望指正。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 下身体痒痒得不得了怎么办 头皮干痒头屑多怎么办 头发掉的露头皮怎么办 头上老是有结痂怎么办 婴儿头上的黄痂怎么办 头皮屑多又痒怎么办小偏方 皮肤瘙痒起红疙瘩怎么办 头又油又痒怎么办 头上反复长脓包怎么办 额头上长了粉刺怎么办 身上长红疙瘩很痒怎么办 脸上的痘痘变硬怎么办 痘痘变成硬疙瘩怎么办 痘痘变硬了怎么办知乎 痘痘里面是硬的怎么办 皮肤出油毛孔大怎么办 蹭无线网信号差怎么办 无线网离得太远怎么办 无线网不好使了怎么办 火疖子化脓破了怎么办 脸上长了个疖子怎么办 脸上起了火疖子怎么办 脸上长了火疙瘩怎么办 脸上长疖子了该怎么办? 头上神经线疼怎么办 打了肉毒发烧了怎么办 不小心吃了活蛆怎么办 吃了生蛆的东西怎么办 我的世界中毒了怎么办 如果人染了尸气怎么办 汽车没油了怎么办紧急 不小心吃氧化钾怎么办 看3d电影没眼镜怎么办 wps宏被禁用了怎么办 京东三天不发货怎么办 京东商家不退款怎么办 苹果保修卡丢了怎么办 手机保修卡丢了怎么办 键盘上没有win键怎么办 用别人id被锁了怎么办 苹果手机被偷了怎么办