C语言通用链表(Linux内核实现)

每次在实现链表的时候,我总是会想起Linux内核链表的实现.今天就来分享一下这个链表的实现.

一般情况下,我们用C语言定义链表时会定义一个数据域和一个指针域,如下面所示:

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

在这种情况,数据域的类型被限制为int,此时的链表定义不具备通用性:如果我们需要一个double类型的链表又需要重新的定义一个新的结构体.这显然不是我们想要的,我们需要一个能够包含任何数据类型的链表.

由于C语言不支持泛型编程(模板编程),我们必须换一种思考方式,既然我们不能定义所有的数据类型的实现,那么是否可以让用户来自定义这种数据类型的实现呢?

下面给出这种数据类型的实现:

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

因为我们不知道用户具体需要存储什么样的数据,此时最好的方式就是让用户来聚合我们的链表.像下面这样:

typedef struct{
	struct list_head node ;
	int data ;
} Data;

看到这里,我想聪明的你一定知道下一步该怎么做了,没错,此时对此结构体的操作只需进行一次强制类型转换就可以使用链表的操作接口了,下面给出部分实现:

#include "stdio.h"
#include "stdlib.h"
// Simple doubly circular linked list implementation.
struct list_head{
	 struct list_head *next, *prev;
};
static inline void list_insert(	struct list_head *new,
							struct list_head *next,
							struct list_head *prev)
{
	new->next = next ;
	new->prev = prev ;
	next->prev = new ;
	prev->next = new ;
}

// get entry by the structure's member offset
#define list_entry(ptr,type,member)	\
	((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
typedef struct{
	struct list_head node ;
	int data ;
} Data;
int main()
{
	struct list_head *head = (struct list_head *)malloc(sizeof(Data));
	head->next = head->prev = head ; 
	struct list_head *node = head ;
	for(int i = 0 ; i < 10 ; ++i)
	{
		struct list_head *h = node ;
		node = (struct list_head *)malloc(sizeof(Data));
		*(int *)list_entry(node,Data,data) = i+1;
		// insert into element to tail
		list_insert(node,h->next,h) ;
	}
	for(struct list_head *pos = head->next;pos != head;pos = pos->next)
	{
		printf("%d\n",*(int *)list_entry(pos,Data,data));
	}
	return 0 ; 
}



从main函数开始,首先分配头结点的空间,将sizeof(Data)的空间强制类型转换为链表的数据类型,以便能够使用链表的接口.接着初始化头结点(双向循环链表),然后向链表尾部插入10个元素,最后遍历这个链表.

list_entry用结构体地址偏移量获取成员的地址,这无疑是一种精妙的做法.上述代码只实现了一个插入函数,其他函数可以用类似的方法实现.

上面的实现思路是受到Linux内核2.6的链表启发而成的,每次看到这里,都使我感觉到C语言的简洁美

猜你喜欢

转载自blog.csdn.net/qq_24663135/article/details/83957722
今日推荐