Linux双向链表(四)——宏

通过前第一,二,三篇双向链表的博文,已经对双链表的基础操作函数做了文字与图片的说明。此博文将要展现操作链表的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
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核中,双向链表是一种特殊的数据结构,它的设计初衷是为了解决不同数据类型作为链表数据节点对函数接口和封装的影响。具体操作双向链表的方法可以通过以下步骤实现: 1. 定义链表节点结构体:在使用双向链表之前,需要先定义链表节点的结构体。这个结构体中需要包含一个前驱指针和一个后继指针,用来连接其他节点。 2. 初始化链表头:在使用链表之前,需要先初始化链表头。链表头是一个特殊的节点,它不存储具体的数据,只是一个用来指向链表的起始位置的指针。 3. 插入节点:要在链表中插入一个新的节点,需要先将新节点的前驱指针和后继指针指向正确的位置,然后将原来的前驱节点和后继节点的指针指向新节点。 4. 删除节点:要从链表中删除一个节点,需要将要删除节点的前驱节点的后继指针指向要删除节点的后继节点,将要删除节点的后继节点的前驱指针指向要删除节点的前驱节点。 5. 遍历链表:要遍历整个链表,可以从链表头开始,一直沿着后继指针向后遍历,直到到达链表尾部。 这些是操作Linux双向链表的一些基本方法,使用这些方法可以进行链表的插入、删除和遍历等操作。同时,Linux内核中还提供了一些定义和函数来更方便地操作双向链表,比如container_of可以通过节点指针获取节点所在的结构体指针。 参考文献: linux 内核双向链表文件 首先介绍下双向链表的相关知识,最后有实战代码 container_of的定义在linux内核include/linux/kernel.h中<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [linux源码内核双向链表文件](https://download.csdn.net/download/qq_18376583/86770056)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [linux 双向链表详解](https://blog.csdn.net/kksc1099054857/article/details/121717238)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值