LIST_ENTRY结构
来源:互联网 发布:全国网络通缉名单2017 编辑:程序博客网 时间:2024/05/02 01:14
在Windows驱动相关编程中,会用到该结构。Windows的源代码中大量使用了该结构。该结构用来组成常见的数据结构——双链表,并且带有头部节点。带头部节点的链表相对于不带头部节点的链表简化了一些链表操作,主要是插入和删除。
LIST_ENTRY
结构如下:
typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink;} LIST_ENTRY, *PLIST_ENTRY;
这是一个最简单的双链表节点,只保存了下一个节点指针Flink(Follow Link)
和前一个节点指针Blink(Before Link)
。我们自己使用链表时会在声明时加入数据域,同时指针域为我们自己的节点类型,例如:
typedef struct _NODE { int data; // 数据域 struct _NODE *Flink; // 后节点指针 struct _NODE *Blink; // 前节点指针}NODE;
两者一对比,就让我们产生一个疑问,LIST_ENTRY
如何加入数据域?
这也是LIST_ENTRY
设计的巧妙处所在。LIST_ENTRY
结构不单独使用,而是在声明其它结构时,将其作为要组成双链表的结构体的一个成员。例如:
typedef struct _NODE { int data1; // 数据域1 LIST_ENTRY ListEntry; int data2; // 数据域2}NODE;
这样,通过ListEntry
成员可以将结构体连接为一个双链表,如下图所示:
头结点不带数据域,为LIST_ENTRY
结构,使用前要用InitializeListHead
函数初始化。
下面还有一个问题,我们通过LIST_ENTRY
结构结构串联起来,使用时通过前后指针获取得到的都是LIST_ENTRY
结构本身,即NODE
结构中的成员ListEntry
。而实际使用时要得到的是NODE
结构本身的指针,这也就是招聘笔试面试过程中常常遇到的一个问题:知道结构体中某个成员的地址,如何获取结构体的起始地址?
讲如何做之前,先看看Windows中怎么获取。就是使用CONTAINING_RECORD
宏,这个宏的原型如下:
PCHAR CONTAINING_RECORD( [in] PCHAR Address, [in] TYPE Type, [in] PCHAR Field);
其中,Type
为结构体类型,Field
为Type
结构中的已知成员,Address
为Field
成员的已知地址,宏的结果为Type
结构的起始地址。对于我们的链表示例,获取链表第一个NODE
结构的起始地址用法为:
CONTAINING_RECORD(ListHead.Flink,NODE,ListEntry)
具体的实现原理比较简单,知道成员的地址,算结构体起始地址,只需要知道成员的偏移,因此,将结构体起始对齐到地址0,然后获取成员的地址,即为成员的偏移量,然后减去偏移量即可,该宏可如下实现:
((Type *)(((ULONG)Address) - (ULONG)(&(((Type *)0)->Field))))
- LIST_ENTRY结构
- LIST_ENTRY结构
- list_entry
- list_entry
- list_entry
- list_entry
- list_entry
- LIST_ENTRY
- list_entry()
- list_entry()
- list_entry
- list_entry()
- list_entry()
- Linux内核中的list_head结构体以及list_entry宏
- 怎样根据结构成员的地址找到结构体的地址(list_entry)
- list_entry 等
- 使用LIST_ENTRY
- list_entry()详解
- vim插件ctags的安装和使用
- c语言L->length与L.length
- 正则表达式
- spring编码过滤器 配置
- 如何在CentOS 5/6上安装EPEL源(备忘)
- LIST_ENTRY结构
- Android内存泄漏(3)
- Linux启动或重启网卡【命令】
- [AndroidAnnotations框架]AndroidAnnotations的配置
- MySQL主从复制简单的介绍
- 测试驱动下的iOS应用开发
- Linux进程状态解析之R、S、D、T、Z、X
- ab压测参数化,可以同时执行多条,脚本仅供参考
- burp suite解决中文乱码