通过前第一,二,三篇双向链表的博文,已经对双链表的基础操作函数做了文字与图片的说明。此博文将要展现操作链表的NB的宏,而且这些宏是最常用的操纵链表的接口,几乎是只要有双链表的地方,就有这些操作宏的使用——而且这群hack,对写宏老专业了,写的很漂亮。不信,往下看!
1、获取包含双链表结点的结构体基地址
换言之,结构体通过嵌入双链表结点实例这种方式组织在一起,就必须能通过嵌入的双链表结点访问整个结构体。而访问整个结构体,就必须知道结构体基地址。
/**
* 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)
通过以下方法得到结构体基地址
1.需要已知三个数据:指向链表结点指针ptr,用户数据类型,用户数据结构中成员变量名。
2.获知用户结构体成员变量在结构体中偏移量
3.依据ptr,计算用户数据数据结构的基址
这里container_of就是干做这个工作的。
<scripts/kconfig/list.h>
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* 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) );})
定义一个member类型指针变量__mptr,赋值为ptr
((size_t) &((TYPE*)0)->MEMBER)
把0强制转化为TYPE指针类型,即0是一个地址,为段基址。取以0为结构体基址的结构体的成员变量MEMBER的地址,那么这个地址就等于MEMBER域到结构体基地址的偏移字节数,并用size_t约束为非负整数.
typeof( ((type *)0)->member )*__mptr = (ptr);
typeof不是标准里面的,是GCC的扩展功能,它能够根据已知变量名来定义和已知变量名数据类型一致的变量。具体使用方式查看GCC手册typeof,这里不详细介绍了。
定义指向 “数据类型和member一致的” 指针变量__mptr,并初始化为ptr值——在双链表这里,由于ptr指向结构体中的链表结点域,因此__mptr也同样指向结构体中的链表结点
(type *)( (char *)__mptr - offsetof(type,member) );
指向结构体链表结点域的指针减去链表结点域相对结构体基地址偏移字节数得到结构体基地址,然后转换成type*类型就得到结构体基地址了!
注意:使用的时候ptr务必是the pointer to the member.
2、获取第一个元素
说明:为了便于讨论,这里说的“元素”即代表包含双链表结点的结构体实例,下同!
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
如果ptr是head,ptr->next就是第一个元素,你完全可以这样理解。因为前文已经提到,无序链表任何一个结点都可以是头。
2.1、获取第一个元素“升级版”——带安全机制
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*
* Note that if the list is empty, it ret