您的位置:首页 > 其它

LIST_ENTRY结构

2016-03-04 13:20 330 查看
在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))))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: