1. 程式人生 > >C語言通用連結串列(Linux核心實現)

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語言的簡潔美