linux循环链表学习

看了linux循环链表,发现实现真的可以这么巧妙

可以用同样的一些链表函数,去操作不同数据的链表,回想一下,通常我们写的链表结构是否如下?

struct _node1
{
     void *data;
     struct _node1 *next;
     struct _node1 *prev;
}NODE1;

struct _node2
{
     void *data;
     struct _node2 *next;
     struct _node2 *prev;
}NODE2;

此时要操作链表,例如add结点,函数是否通常定义为:

//将new结点添加到pos的后面(或前面)
 int list_add(NODE1 *pos, NODE1 *new)

ok,此时,如果你要添加的不是NODE1类型的结点,反而是另一种,如NODE2,那么上面的函数是不是就不适用了,你要重新写一个函数接口

int list_add(NODE2 *pos, NODE2 *new)

如果你要操作的再是存储另外类型的链表,那么是不是都要写一整套同样的函数,只是参数不同而已,好麻烦。肯定也很多人在想,我能不能实现一套函数接口,使得可以操作所有类型的链表呢?答案是可以的。linux内核双向循环链表的实现就是如此巧妙。

我们定义链表结点如下:

struct _head_list
{
     struct _head_list *next;
     struct _head_list *prev;
}head_list;


struct _linux_node
{
      void *data;
      head_list linux_list;//这个就是巧妙之处1
}linux_node;


我们添加一个add函数如下,注意参数类型哦,

int list_add(head_list *new, head_list *pos);

//调用方式如下
struct _linux_node node_new;
head_list data_list;
list_add(&node_new, &data_list);

这时有人会奇怪,你添加的并不是struct _linux_node类型,那么里面的data添加进去了吗,怎么能够取出来data啊。ok,接着是linux结点的最强大之处了,我们先看几个宏,

#define list_entry(ptr, type, member) \
		((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

解释一下这个宏,ptr为指向宿主类型(即struct _linux_node)的指针,type即struct _linux_node,member为struct _linux_node中的成员linux_list.

&(((type *)0)->member),将0转为指向type类型的指针,0为内存手地址,那么member的地址就为member偏离宿主结点起始地址的大小,

(char *)(ptr)-(unsigned long)(&(((type *)0)->member))即为宿主结构的起始地址了。

struct _linux_node *tmp_data = NULL;

tmp_data = list_entry(pos, linux_node, linux_list);

tmp_data得到的就是指向linux_node类型的指针,这个时候我们就可以用tmp_data->data来取得结点中的数据了。


下面贴上自己学习过程中实现的linux_list.c, linux_list.h,test.c代码,test代码写的有点乱,大家将就着看吧。

linux_list.h

/*
 * linux_list.h
 *
 *  Created on: Jul 30, 2012
 *      Author: Carl
 */

#ifndef LINUX_LIST_H_
#define LINUX_LIST_H_

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

//这个宏已经在stddef.h中定义了
//#define offsetof(type, member)		((size_t)&((type *)0)->member)

//通过这个宏可以得到宿主结构体的指针,这个是linux链表的真正强大之处
#define list_entry(ptr, type, member) \
		((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

//pos为struct list_head类型的指针
#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

#define __list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

#define list_for_each_prev(pos, head) \
	for (pos = (head)->prev; pos != (head); pos = pos->prev)

#define __list_for_each_prev(pos, head) \
	for (pos = (head)->prev; pos != (head); pos = pos->prev)

//safe仅仅体现在,可以遍历的同时删除pos结点,但删除非pos结点是unsafe的
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next)

//pos为指向宿主类型的指针,head是头结点,即data_list,member是宿主成员struct list_head list
#define list_for_each_entry(pos, head, member)	\
	for (pos = list_entry((head)->next, typeof(*pos), member);		\
		  &pos->member != (head);		\
		 pos = list_entry(pos->member.next, typeof(*pos), member))

#define list_for_each_entry_reverse(pos, head, member)		\
	for (pos = list_entry((head)->prev, typeof(*pos), member);		\
		 &pos->member != (head);			\
		pos = list_entry(pos->mmeber.prev, typeof(*pos), member))

#define list_prepare_entry(pos, head, member) 		\
	((pos) ? :list_entry(head, typeof(*pos), member))

#define list_for_each_entry_continue(pos, head, member)		\
	for (pos = list_entry(pos->member.next, typeof(*pos), member);		\
		 &pos->member != (head);			\
		pos = list_entry(pos->member.next, typeof(*pos), member))


#define list_for_each_entry_safe(pos, n, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member), n = list_entry(pos->member.next, typeof(*pos), member); \
		&pos->member != (head);										 \
		pos = n, n = list_entry(n->member.next, typeof(*pos), member))

//链表初始化
void list_init(struct list_head *list);

//在head之后插入新结点,head不一定是头结点
void list_add(struct list_head *new, struct list_head *head);

//在head之前插入新结点,如果head为头结点,那么head->prev要指向一个数值,一般指向尾结点,构成循环链表
void list_add_tail(struct list_head *new, struct list_head *head);

//删除结点entry
void list_del(struct list_head *entry);

//删除结点entry,并用entry来初始化创建新的链表
void list_del_init(struct list_head *entry);

//将list结点移动到head和head->next两个结点之间
void list_move(struct list_head *list, struct list_head *head);

//将list结点移动到head->prev和head两个结点之间
void list_move_tail(struct list_head *list, struct list_head *head);

//判断链表是否为空
int list_empty(const struct list_head *head);
int list_empty_careful(const struct list_head *head);

//将list插入到head中,head和head->next之间,list的链表头被抛弃了
void list_splice(struct list_head *list, struct list_head *head);

//将list插入到head中,并初始化list被抛弃的链表头
void list_splice_init(struct list_head *list, struct list_head *head);

#endif /* LINUX_LIST_H_ */

linux_list.c

/*
 * linux_list.c
 *
 *  Created on: Jul 30, 2012
 *      Author: Carl
 */


//#include <stdio.h>
#include <stdlib.h>
#include "linux_list.h"




void list_init(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

// 在两个非空结点中插入一个结点,new, prev, next均不能为空
static void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
{
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}


void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}


void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

//删除prev和next之间的结点
static void __list_del(struct list_head *prev, struct list_head *next)
{
	next->prev = prev;
	prev->next = next;
}


void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->prev = NULL;
	entry->next = NULL;
}


void list_del_init(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	list_init(entry);
}


void list_move(struct list_head *list, struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add(list, head);
}


void list_move_tail(struct list_head *list, struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add_tail(list, head);
}

int list_empty(const struct list_head *head)
{
	return head->next == head;
}

int list_empty_careful(const struct list_head *head)
{
	return (head->next == head) && (head->prev == head);
}

static void __list_splice(struct list_head *list, struct list_head *head)
{
	struct list_head *first = list->next;
	struct list_head *last = list->prev;
	struct list_head *at = head->next;

	first->prev = head;
	head->next = first;

	last->next = at;
	at->prev = last;
}

void list_splice(struct list_head *list, struct list_head *head)
{
	if (!list_empty(list))
	{
		__list_splice(list, head);
	}
}

void list_splice_init(struct list_head *list, struct list_head *head)
{
	if (!list_empty(list))
	{
		__list_splice(list, head);
		list_init(list);
	}
}

test.c

/*
 * test.c
 *
 *  Created on: Jul 30, 2012
 *      Author: Carl
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linux_list.h"

typedef struct _data
{
	int version;
	char str[20];
	struct list_head list;
}data;



int NODE_NUM =	5;

int main()
{
	data *pdata = NULL, *pdata2 = NULL;
	data *tmp_data = NULL;
	struct list_head data_list, data_list2;
	struct list_head *pos = NULL, *n = NULL;
	int i = 0;

	list_init(&data_list);
	list_init(&data_list2);

	pdata = (data *)malloc(sizeof(data) * 5);
	if (pdata == NULL)
	{
		printf("malloc for pdata error\n");
		return -1;
	}

	pdata2 = (data *)malloc(sizeof(data) * 5);
	if (pdata2 == NULL)
	{
		printf("malloc for pdata2 error\n");
		return -1;
	}

	memset(pdata, 0, sizeof(data));
	memset(pdata2, 0, sizeof(data));
	for (i = 0; i < NODE_NUM; i++)
	{
		sprintf(pdata[i].str, "version%d", i+1);
		sprintf(pdata2[i].str, "version%d", i+4);
		pdata[i].version = i+1;
		pdata2[i].version = i+4;
		list_add(&(pdata[i].list), &data_list);
		list_add(&(pdata2[i].list), &data_list2);
	}

	list_for_each_entry(tmp_data, &data_list, list)
	{
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
	}

	goto End;

	printf("data_list 链表\n");
//	for(pos = data_list.next, i = 0; pos != NULL && i < NODE_NUM; pos = pos->next, i++)
	list_for_each_safe(pos, n, &data_list)
	{
		tmp_data = list_entry(pos, data, list);
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
		list_del(pos);
	}

	printf("data_list2 链表\n");
	for(pos = data_list2.next, i = 0; pos != NULL && i < NODE_NUM; pos = pos->next, i++)
	{
		tmp_data = list_entry(pos, data, list);
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
	}

	list_splice(&data_list, &data_list2);

	printf("data_list, data_list2合并以后\n");
	for(pos = data_list2.next; pos != &data_list2; pos = pos->next)
	{
		tmp_data = list_entry(pos, data, list);
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
	}

	goto End;

	//循环读出list中每个结点的数据部分
	for(pos = data_list.next, i = 0; pos != NULL && i < NODE_NUM; pos = pos->next, i++)
	{
		tmp_data = list_entry(pos, data, list);
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
	}

	if (list_empty(&data_list))
	{
		printf("使用list_empty()检测,链表为空\n");
	}
	else
	{
		printf("使用list_empty()检测,链表非空\n");
	}

	if (list_empty_careful(&data_list))
	{
		printf("使用list_empty_careful()检测,链表为空\n");
	}
	else
	{
		printf("使用list_empty_careful()检测,链表非空\n");
	}

//	list_del(&pdata[3].list);
//	NODE_NUM--;
//	printf("pdata[3] is deleted\n");

	list_move(&pdata[3].list, &data_list);
	printf("将pdata[3]移至head和head->next两个结点之间\n");
	//循环读出list中每个结点的数据部分
	for(pos = data_list.next, i = 0; pos != NULL && i < NODE_NUM; pos = pos->next, i++)
	{
		tmp_data = list_entry(pos, data, list);
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
	}

//	list_del_init(&pdata[2].list);
//	printf("pdata[2] is deleted\n");
//	NODE_NUM--;

	list_move_tail(&pdata[2].list, &data_list);
	printf("将pdata[2]移至head->prev和head两个结点之间\n");
	//循环读出list中每个结点的数据部分
	for(pos = data_list.next, i = 0; pos != NULL && i < NODE_NUM; pos = pos->next, i++)
	{
		tmp_data = list_entry(pos, data, list);
		printf("version: %d, \tversion str: %s\n", tmp_data->version, tmp_data->str);
	}

End:
	free(pdata);

	return 0;

}


建议代码一定要自己写一遍,千万不要直接粘贴复制编译运行。


水平有限,如果有朋友发现错误,欢迎留言交流。
转载请保留本文链接,如果觉得我的文章能帮到您,请顶一下。,谢谢。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值