`

结构体内变量相对便宜与list_entry()宏

    博客分类:
  • c++
 
阅读更多

 

#define list_entry(ptr, type, member) \

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

 

    ptr是指向list_head类型链表的指针,type为一个结构,而member为结构type中的一个域,类型为list_head,这个宏返回指向type结构的指针。在内核代码中大量引用了这个宏,因此,搞清楚这个宏的含义和用法非常重要。

 

 

设有如下结构体定义:

typedef struct xxx

{

     ……(结构体中其他域,令其总大小为size1)

     type1 member;

     ……(结构体中其他域)

}type;

 

 

定义变量:

   type a;

   type * b;

   type1 * ptr;

执行:

   ptr=&(a.member);

   b=list_entry(ptr,type,member);

则可使b指向a,得到了a的地址。

 

如何做到的呢?

 

先看&((type *)0)->member:

把“0”强制转化为指针类型,则该指针一定指向“0”(数据段基址)。因为指针是“type *”型的,所以可取到以“0”为基地址的一个type型变量member域的地址。那么这个地址也就等于member域到结构体基地址的偏移字节数。

 

再来看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):

(char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type *)0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针,大功告成。

 

--------------------------------------------------------------------------------------------------

 

/**

 

* 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) \

 

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

 

--------------------------------------------------------------------------------------------------

 

指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址,如图2。

 

 

 

type

 

        |----------|

 

        | |

 

          | |

 

          |----------|

 

ptr--> | member --|

 

          |----------|

 

          | |

 

        | |

 

         |----------|

 

        图2 list_entry()宏的示意图

 

为了便于理解,在此给予进一步说明。

 

例如my_list结构:

 

struct my_list{

 

void *mydata;

 

struct list_head list;

 

}; struct list_head *pos;  

 

则list_entry(pos, mylist, list)宏,就可以根据pos的值,获取mylist的地址,也就是指向mylist的指针,这样,我们就可以存取mylist->mydata字段了。

 

可为什么能够达到这样的效果?

 

list_entry(pos, mylist, list) 展开以后为:

 

((struct my_list *)((char *)(pos) - (unsigned long)(&((struct my_list *)0)->list)))

 

这看起来会使大多数人眩晕,但仔细分析一下,实际很简单。

 

((size_t) &(type *)0)->member)把0地址转化为type结构的指针,然后获取该结构中member成员的指针,并将其强制转换为size_t类型。于是,由于结构从0地址开始定义,因此,这样求出member的成员地址,实际上就是它在结构中的偏移量。

分享到:
评论

相关推荐

    深入浅出linux内核源代码之双向链表list_head.pdf

    这样,每个 `person` 或 `animal` 实例不仅包含了年龄和体重等数据成员,还能通过 `list` 成员与其他实例形成双向链表结构。 #### 三、`list_head` 相关函数介绍 `<linux/list.h>` 头文件中提供了一系列用于操作...

    内核数据结构之双向循环链表

    本篇文章将深入探讨这种数据结构,包括`list_head`、`list_entry`以及相关的遍历宏`list_for_each`。 首先,我们要理解什么是双向循环链表。不同于单向链表,双向循环链表中的每个节点不仅包含指向下一个节点的指针...

    Linux内核list&hlist;解读

    本文将详细介绍 Linux 内核中的两种主要链表结构:list 和 hlist,并解释与之相关的宏及其用法。 #### 2. 通用宏 ##### 2.1. typeof **2.1.1. 定义** `typeof` 并不是一个宏,而是 GCC 的一个内建操作符,用于...

    算法学习资料:详解Linux内核之双向循环链表

    list_head 结构可以作为一个成员嵌入到宿主数据结构内,可以将链表结构放在宿主结构内的任何地方,宿主结构可以有多个链表结构,并可以用 list_head 中的成员和相对应的处理函数来对链表进行遍历。 3. 定义和初始化...

    Python Tkinter Entry和Text的添加与使用详解

    本篇文章将详细讲解Tkinter中的两个关键组件——Entry和Text,并通过示例代码展示它们的添加与使用方法。 首先,我们来看Entry组件。Entry是用于创建单行文本输入框的控件,用户可以在其中输入或修改文本。在示例...

    UEFI中Protocol的一些介绍

    在UEFI中,Protocol的组织方式是通过LIST_ENTRY结构体来实现的,LIST_ENTRY结构体将IHANDLE、PROTOCOL_ENTRY、PROTOCOL_INTERFACE、PROTOCOL_NOTIFY这些结构体“串连”起来,形成一个链表。这样,我们可以通过LIST_...

    Linux内核链表(移植完成)

    - `list_entry(ptr, type, member)`:通过成员变量`member`从结构体指针`ptr`获取`type`类型的对象指针。 - `list_for_each(pos, head)`:遍历链表`head`,用`pos`迭代每个节点。 - `list_for_each_entry(pos, ...

    Linux内核通用链表linuxlist.h阅读.pdf

    链表的遍历通常使用`list_for_each_entry`和`list_for_each_entry_reverse`等循环宏,它们允许我们方便地访问链表中的每一个元素。在内核编程中,这样的链表操作非常常见,例如用于管理内存分配的slab缓存系统、设备...

    内核链表.pdf

    内核链表还提供了用于访问链表节点的宏,比如list_entry宏可以根据list_head结构的地址,计算出宿主数据结构的地址。这些宏在遍历链表和操作链表时非常有用。 在实际使用中,内核链表能够高效地对数据进行管理,...

    TCP-IP详解卷2:实现.part1

    本书不仅说明了插口API和协议族的关系以及主机实现与路由器实现的差别。还介绍了4.4BSD-Lite版的新的特点,如多播、长肥管道支持、窗口缩放、时间戳选项以及其他主题等等。读者阅读本书时,应当具备卷1中阐述的关于...

    WRK_内核读取全部进程_调研报告

    在这个结构体中,有一个名为ActiveProcessLinks的LIST_ENTRY变量,它是EPROCESS结构体在系统中所有进程列表中的链接字段。这个列表是由内核维护的,用于跟踪系统中的每一个活动进程。 LIST_ENTRY是一个双向链表结构...

    LINUX 下的内核链表

    遍历内核链表可以使用`for_each_entry`或`for_each_entry_safe`宏。前者在遍历时不允许删除当前节点,而后者通过临时变量保护,可以在遍历过程中安全地删除节点。 5. **链表操作的原子性**: 在多线程环境下,...

    电话本设计的基本流程

    - **g_mmipb_entry_list**:表示存储器中排序和entry_id使用情况的数据结构。 - `MMIPB_PHONEBOOK_SORT_T sort_t`:用于排序的表格。 - `MMIPB_ENTRY_ID_T sim_entry_id_t`:SIM卡中entry_id的使用情况。 - `...

    内核Linux链表的实现.zip

    `list_for_each_entry(pos, head, member)`循环遍历链表,`pos`是链表节点类型变量,`head`是链表头,`member`是链表节点类型中包含`list_head`的成员名。这确保了即使在遍历过程中删除节点,也不会发生未定义的行为...

    学生考试专用java试卷一.pdf

    文件中提到了list_head结构体,这是内核链表的节点结构,而list_for_each()、list_entry()等宏用于遍历链表和从链表中提取元素。这些宏是内核中常用的链表操作工具。 由于提供的文件片段中存在部分OCR扫描错误和不...

    PHP100视频教程79:PHP上传RAR压缩包并解压目录.rar

    接下来,你可以遍历RAR文件中的所有条目,使用`rar_list`函数获取条目列表,并使用`rar_entry_get`获取特定条目。如果条目是目录,可以使用`rar_entry_extract`解压到服务器上的目标目录: ```php $entries = rar_...

    易语言学习-PE加载器(PeLoader)支持库版.zip

    7. 易语言语法:熟悉易语言的基本语法规则,如变量声明、函数调用、流程控制等。 8. 实践应用:通过编写简单的程序,练习使用PeLoader支持库来加载和控制PE文件,提升对Windows程序运行机制的理解。 9. 安全与反反...

    Freemarker中遍历list集合实例

    3. **长度信息**:`size`内置变量可以获取list的长度,`last`变量表示是否是最后一个元素。 ```freemarker <#list users as user> 这是最后一个用户: ${user.name} </#list> ``` 4. **遍历子列表**:`...

    linux下PCI驱动开发

    其中,id_table可以使用宏PCI_DEVICE(VENDOR_ID, DEVICE_ID)进行初始化,VENDOR_ID和DEVICE_ID分别是设备和厂商编号,由板卡生产厂家指定。 3. struct pci_dev数据结构 struct pci_dev是Linux内核中用于描述PCI...

Global site tag (gtag.js) - Google Analytics