设为首页 加入收藏

TOP

C语言的那些小秘密之链表(四) (二)
2014-11-23 22:30:40 来源: 作者: 【 】 浏览:4
Tags:语言 些小 秘密
list_empty(list)) {

__list_splice(list, head);

INIT_LIST_HEAD(list);

}

}

以上函数的功能是将一个链表list的有效信息合并到另外一个链表head后,重新初始化被去掉的空的链表头。这样的描述可能不是太好理解,接下来看看一段代码。

#include

#include

#include "list.h"

typedef struct _stu

{

char name[20];

int num;

struct list_head list;

}stu;

int main()

{

stu *pstu,*pstu2;

stu *tmp_stu;

struct list_head stu_list,stu_list2;

struct list_head *pos;

int i = 0;

INIT_LIST_HEAD(&stu_list);

INIT_LIST_HEAD(&stu_list2);

pstu = malloc(sizeof(stu)*3);

pstu2 = malloc(sizeof(stu)*3);

for(i=0;i<3;i++)

{

sprintf(pstu[i].name,"Stu%d",i+1);

sprintf(pstu2[i].name,"Stu%d",i+4);

pstu[i].num = i+1;

pstu2[i].num = i+4;

list_add( &(pstu[i].list), &stu_list);

list_add( &(pstu2[i].list), &stu_list2);

}

printf("stu_list 链表\n");

list_for_each(pos,&stu_list)

{

tmp_stu = list_entry(pos, stu, list);

printf("student num: %d\tstudent name: %s\n",tmp_stu->num,tmp_stu->name);

}

printf("stu_list2 链表\n");

list_for_each(pos,&stu_list2)

{

tmp_stu = list_entry(pos, stu, list);

printf("student num: %d\tstudent name: %s\n",tmp_stu->num,tmp_stu->name);

}

printf("stu_list链表和stu_list2 链表合并以后\n");

list_splice(&stu_list2,&stu_list);

list_for_each(pos,&stu_list)

{

tmp_stu = list_entry(pos, stu, list);

printf("student num: %d\tstudent name: %s\n",tmp_stu->num,tmp_stu->name);

}

free(pstu);

return 0;

}

运行结果为:

root@ubuntu:/home/paixu/dlist_node# ./a

stu_list 链表

student num: 3 student name: Stu3

student num: 2 student name: Stu2

student num: 1 student name: Stu1

stu_list2 链表

student num: 6 student name: Stu6

student num: 5 student name: Stu5

student num: 4 student name: Stu4

stu_list链表和stu_list2 链表合并以后

student num: 6 student name: Stu6

student num: 5 student name: Stu5

student num: 4 student name: Stu4

student num: 3 student name: Stu3

student num: 2 student name: Stu2

student num: 1 student name: Stu1

有了直观的代码和运行结果,理解起来也更加的容易了。

有了上面的这些操作,但是我们还一直没有讲到我们最终所关心的宿主结构,那么接下来我们一起来看看我们该如何取出宿主结构的指针呢?这也是我认为linux内核双向循环链表实现最为巧妙的地方了。

#define list_entry(ptr, type, member) \

((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

看看上面的代码,发现一个很熟悉的身影(unsigned long)(&((type *)0)->member)),这个我在前一篇博客《C语言的那些小秘密之字节对齐》中已经讲解过了,多以在此就不再做过多的讲解,如果有不明白的读者可以回过去看看讲解再回过来阅读。通过(unsigned long)(&((type *)0)->member))我们得出了成员变量member的偏移量,而ptr为指向member的指针,因为指针类型不同的原因,所以我们再次要先进行(char*)的转换之后再进行计算。所以我们用ptr减去member的偏移量就得到了宿主结构体的指针,这就是一个非常巧妙的地方,这也就使得linux内核双向循环链表能够区别于传统链表的关键所在。可能看到这儿的时候读者已经感觉非常的枯燥了,但是别放弃,坚持看完,因为虽然这样的讲解枯燥了点,但是非常有用。所以坚持坚持吧!

#define list_for_each(pos, head) \

for (pos = (head)->next; prefetch(pos->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)

首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C语言的那些小秘密之链表(三) 下一篇让你不再害怕指针0--复杂类型说明

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: