C语言中如何使用通用双向链表

通用双向链表,指的是通过一个通用的数据结构,然后添加特定数据形成有具体用途的链表。这主要涉及到代码复用,在C++中实现非常方便,只需要定义一个双向链表的类,每一个具体业务只需要继承它,然后添加自己的数据成员即可。但有时候我们需要用C语言而不是C++,那么该如何实现这种层次关系呢?

首先我们来看两个宏。

第一个是offsetof(可参考Linux内核代码include/linux/Stddef.h):

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这个宏什么意思呢,它返回MEMBER成员在结构体TYPE中的偏移量,下面举个例子说明,假设有个结构体A如下:

struct struct_A
{
    int a[2];
    int b;
}

其中,成员a相对于结构的偏移量为0,成员b相对于结构体的偏移量为8;结构体struct_A的变量m在内存中地址结构如下图所示:

C语言中如何使用通用双向链表_第1张图片

对地址0取b成员,然后再取地址,减去结构体首地址就是偏移量,由于首地址是0,故这个结果就是偏移量8。

第二个宏是container_of(可参考Linux内核代码include/linux/Kernel.h):

#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    (type *)( (char *)__mptr - offsetof(type,member) );})

这个宏先把ptr指针转化为char指针,这样指针移动操作单位为字节;然后减去其在结构体中的偏移量,就能回到ptr所在的结构体的开头,即得到了结构体指针,最后强制转换成目标类型指针,就得到了ptr指针所在结构体变量的首地址,即得到一个type结构体指针。

接着进入正文。

我们已经得到从结构体某一成员地址反推结构体指针的办法,就可以通过把“通用数据结构”作为结构体成员的办法,来构建一个带业务功能的双向链表,而不是一般思路中的链表中包含数据的方法。

假设一个双向链表如下:

struct list_head
{
    struct list_head *next, *prev;
};

然后我们创建一个雇员信息结构:

struct employee
{
    char name[32];
    int age;
    struct list_head *listnode;
};

创建雇员信息节点时,需要初始化雇员信息结构,然后在链表中添加一个节点,最后把雇员信息结构中的链表指针指向新添加的链表节点,这样就完成了雇员列表的创建。
当需要取一个雇员信息时,首先确定链表节点,然后通过container_of宏取得雇员信息。比如以下代码:

struct list_head *ptr_node = NULL;
struct employee *emp_info = NULL;
…
ptr_node = x;
emp_info = container_of(ptr_node, struct employee, listnode);

你可能感兴趣的:(语言与算法,Linux)