list_for_each,list_for_each_entry和list_for_each_entry_safe

比较

  • 共同点
    1.list_for_each和list_for_each_entry都是遍历链表的两个宏,本质上都是for循环。
    2.他们做的事情本质上都一样,A.获取链表头,B.判断链表项是不是链表头,C.指向链表的下一项。
    3.他们的区别:list_for_each遍历的链表,其链表项不属于某个结构体。或者说不关心它是不是包含在某个结构体中。
  • 区别
    1.list_for_each_entry遍历的链表,其每一项都是某个结构体中的成员,单纯遍历链表还不行,还要找到包含这个链表项的结构体的地址,从而为下一步应用该结构体做好准备,遍历的每一项为一个结构体。
    2.相比于list_for_each_entry,list_for_each_entry_safe用指针n对链表的对下一个数据结构进行了临时存储,所以如果在遍历链表的时候可能要删除链表的当前项,用list_for_each_entry_safe可以安全的删除,而不会影响接下来的遍历过程。

1 list_for_each

list_for_each内核中的定义:
/**
 * list_for_each - iterate over a list
 * @pos: the &struct list_head to use as a loop cursor.
 * @head: the head for your list.
 */
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)

一个简单的for循环。

循环的初始化工作:pos指向链表头的下一项。

循环的条件:pos不是链表头。

每次循环要做的事情:pos指向链表中的下一项

2 关于list_for_each_entry相关函数

2.1 offsetof宏

#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)

该宏先将0转换成TYPE型指针,即形成一个指向地址0的TYPE指针,然后对TYPE中的MEMBER成员进行取址,而整个TYPE结构体的起始地址是0,那么这里取得的MEMBER的地址实际上等同于在TYPE中的相对偏移量。

2.2 container_of

/**
* container_of - cast a member of a structure out to the containing structure
* @ptr:        the pointer to the member.
* @type:       the type of the container struct this is embedded in.
* @member:     the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({                    \
     const typeof( ((type *)0)->member ) *__mptr = (ptr);     \
     (type *)( (char *)__mptr - offsetof(type,member) );})      

可以看到container_of被预定义成一个函数,它首先通过((type *)0)->member定义member类型的指针__mptr,这个指针指向ptr,获取到了我们所要求的结构体所包含的member的地址,然后(char *)__mptr - offsetof(type, member),通过member成员的地址减去它在结构体中的偏移量,然后强制转换成type指针就得到了这个结构体的地址,define预定义返回最后一句表达式的值,将所求结构体指针返回。
  总结一下,container_of的功能就是通过一个指向结构体成员member的指针,求得指向整个结构体的指针。

2.3 list_entry

/**
* list_entry - get the struct for this entry
* @ptr:     the &struct list_head pointer.
* @type:     the type of the struct this is embedded in.
* @member:     the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
       container_of\
       (ptr,type,member) 

从定义中可以看到,list_entry其实是container_of的一个别名而已,完全等同

2.4 list_for_each_entry

/**
* list_for_each_entry     -     iterate over list of given type
* @pos:      the type * to use as a loop cursor.
* 这里要传入结构体指针,因为pos = list_entry((head)->next, typeof(*pos), member),list_entry返回的是结构
* 体指针, typeof(*pos) 是结构体的类型
* @head:     the head for your list.
* head是传入的是struct list_head 类型的指针,一般是LIST_HEAD过的只有pre和next的头,因为要作为遍历,head-*>next就是指向member的
* @member:   the name of the list_struct within the struct.
* member一般是pos结构体里面的list_header类型的成员
*/
#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))

这里强调一下双向链表及链表头的概念,建立一个双向链表通常有一个独立的用于管理链表的链表头,链表头一般不包含实体数据的,必须使用INIT_LIST_HEAD()进行初始化,表头建立后,就可以将带有数据结构的实体链表成员加入到链表张。关系如图所示。

在这里插入图片描述
list_for_each_entry被预定义为一个for循环语句,for循环的第一句获取(head)->next指向的member成员的结构体指针,将pos初始化为链表中除链表头之外的第一个实体链表成员,for的第三句通过pos->member.next指针遍历整个实体链表,当pos->member.next再次指向链表头的时候,说明已经遍历完毕,退出循环。

3 list_for_each_entry_safe

/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos:     the type * to use as a loop cursor.
* @n:       another type * to use as temporary storage
* @head:    the head for your list.
* @member:  the name of the list_struct within the struct.
*/
#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(*n), member))

相比于list_for_each_entry,list_for_each_entry_safe用指针n对链表的对下一个数据结构进行了临时存储,所以如果在遍历链表的时候可能要删除链表的当前项,用list_for_each_entry_safe可以安全的删除,而不会影响接下来的遍历过程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值