设为首页 加入收藏

TOP

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

for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \

pos = pos->prev)

遍历是双循环链表的基本操作,head为头节点,遍历过程中首先从(head)->next开始,当pos==head时退出,故head节点并没有访问,这和链表的结构设计有关,通常头节点都不含有其它有效信息,因此可以把头节点作为双向链表遍历一遍的检测标志来使用。在list_for_each宏中读者可能发现一个比较陌生的面孔,我们在此就不将prefetch展开了讲解了,有兴趣的读者可以自己查看下它的实现,其功能是预取内存的内容,也就是程序告诉CPU哪些内容可能马上用到,CPU预先其取出内存操作数,然后将其送入高速缓存,用于优化,是的执行速度更快。接下来的__list_for_each()宏和list_for_each_prev()宏就不在此做一一的讲解了,和list_for_each()宏类似。 就是遍历的方向有所改变或者不使用预取。

通过上面的讲解和前面那么多的代码,相信读者这个时候对于list_for_each()已经不再感到陌生了。上面的但三个遍历链表的宏都类似,继续往下看。

#define list_for_each_safe(pos, n, head) \

for (pos = (head)->next, n = pos->next; pos != (head); \

pos = n, n = pos->next)

以上list_for_each_safe()宏也同样是用于遍历的,不同的是里边多出了一个n暂存pos下一个节点的地址,避免了因为pos节点被释放而造成的断链,这也就体现出了safe。也就是说你可以遍历完当前节点后将其删除,同时可以接着访问下一个节点,遍历完毕后就只剩下一个头节点。当然这有一个最为典型的应用,那就是我们在多进程编程时候,多个进程等待在同一个等待队列上,若事件发生时唤醒所有进程,则可以唤醒后将其依次从等待队列中删除。

#include

#include

#include "list.h"

typedef struct _stu

{

char name[20];

int num;

struct list_head list;

}stu;

int main()

{

stu *pstu;

stu *tmp_stu;

struct list_head stu_list;

struct list_head *pos,*n;

int i = 0;

INIT_LIST_HEAD(&stu_list);

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

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

{

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

pstu[i].num = i+1;

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

}

printf("通过list_for_each_safe()遍历使用list_del(pos)删除结点前\n");

list_for_each_safe(pos, n, &stu_list)

{

tmp_stu = list_entry(pos, stu, list);

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

list_del(pos);

}

printf("通过list_for_each()遍历使用list_del(pos)删除结点后\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);

}

free(pstu);

return 0;

}

运行结果为:

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

通过list_for_each_safe()遍历使用list_del(pos)删除结点前

student num: 3 student name: Stu3

student num: 2 student name: Stu2

student num: 1 student name: Stu1

通过list_for_each()遍历使用list_del(pos)删除结点后

读者可以结合运行结果再去阅读上面的文字描述部分。

如果只提供对list_head结构的遍历操作是远远不够的,我们希望实现的是对宿主结构的遍历,即在遍历时直接获得当前链表节点所在的宿主结构项,而不是每次要同时调用list_for_each()和list_entry()。为此Linux特地提供了list_for_each_entry()宏来实现

#define list_for_each_entry(pos, head, member) \

for (pos = list_entry((head)->next, typeof(*pos), member); \

prefetch(pos->member.next), &pos->member != (head); \

pos = list_entry(pos->member.next, typeof(*pos), member))

第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三个参数为list_head结构在宿主结构中的成员名。有时候做过多的讲解反而没有看看代码的效果好,我们还是用段代码来说明下吧。

#include

#include

#include "list.h"

typedef struct _stu

{

char name[20];

int num;

struct list_head list;

}stu;

int main()

{

stu *pstu;

stu *tmp_stu;

struct list_head stu_list;

struct list_head *pos,*n;

int i = 0;

INIT

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

评论

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