LIST_ENTRY链表学习


链表是驱动开发中经常遇到的一个数据结构,主要是双向循环链表;要使用链表,需要用到一个LIST_ENTRY的结构,其定义如下:

typedef struct _LIST_ENTRY {
    struct _LIST_ENTRY  *Flink;    // 指向下一个节点
    struct _LIST_ENTRY  *Blink;    // 指向前一个节点
} LIST_ENTRY, *PLIST_ENTRY;


每个双向链表都是一个以链表头作为链表第一个元素,初次使用时需要进行初始化,主要讲链表头的Flink,Blink指向自己,连表头所代表的是空链。初始化时用InitializeListHead宏实现。


常用操作:

InitializeListHead,初始化链表头 
IsListEmpty,判断链表是否为空 
InsertHeadList,从链表头部插入节点 
InsertTailList,从链表尾部插入节点 
RemoveHeadList,从链表头部删除节点 
RemoveTailList,从链表尾部删除节点 
CONTAINING_RECORD,从RemoveHeadList或者RemoveTailList返回的数据获取一个指向删除节点的指针 
实例代码(from windows 驱动开发技术详解)

#include <ntddk.h>

typedef struct _MYDATASTRUCT 
{
	ULONG number;
	LIST_ENTRY ListEntry;
}MYDATASTRUCT, *PMYDATASTRUCT;

VOID LinkListTest()
{
	LIST_ENTRY linkListHead;
	//初始化链表
	InitializeListHead(&linkListHead);
	PMYDATASTRUCT pData;
	ULONG i = 0;
	//在链表中插入10个元素
	KdPrint(("Begin insert to link list\n"));
	for(i=0; i<10; i++)
	{
		//分配页内存
		pData = (PMYDATASTRUCT)ExAllocatePool(PagedPool, sizeof(MYDATASTRUCT));
		pData->number = i;
		//从头部插入链表
		InsertHeadList(&linkListHead, &pData->ListEntry);
	}
	//从链表中取出,并显示
	KdPrint(("Begin remove from link list\n"));
	while(!IsListEmpty(&linkListHead))
	{
		//从尾部删除一个元素
		PLIST_ENTRY pEntry = RemoveTailList(&linkListHead); //返回删除结构中ListEntry的位置
		pData = CONTAINING_RECORD(pEntry,
								MYDATASTRUCT,
								ListEntry);
		KdPrint(("%d \n",pData->number));
		ExFreePool(pData);
	}
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
	KdPrint(("Enter DriverUnload...\n"));
}

NTSTATUS 
	DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath) 
{
	KdPrint(("Enter DriverEntry\n"));
	pDriverObject->DriverUnload = DriverUnload;
	LinkListTest();
	KdPrint(("Exit DriverEntry\n"));
	return STATUS_SUCCESS;
}

下面用windbg跟了下看看:

在InitializeListHead函数之前,查看 linkListHead

kd> dt linkListHead
Local var @ 0xf78eec78 Type _LIST_ENTRY
 [ 0x8 - 0x386 ]
   +0x000 Flink            : 0x00000008 _LIST_ENTRY
   +0x004 Blink            : 0x00000386 _LIST_ENTRY

可知Flink、Blink随机给了两个值

InitializeListHead函数执行后

kd> dt linkListHead
Local var @ 0xf78eec78 Type _LIST_ENTRY
 [ 0xf78eec78 - 0xf78eec78 ]
   +0x000 Flink            : 0xf78eec78 _LIST_ENTRY [ 0xf78eec78 - 0xf78eec78 ]
   +0x004 Blink            : 0xf78eec78 _LIST_ENTRY [ 0xf78eec78 - 0xf78eec78 ]

可知结构中两个成员都指向自己。

跟踪程序,当程序中链表有两个元素后(除了表头),查看下链表情况

链头:

kd> dt linkListHead
Local var @ 0xf78eec78 Type _LIST_ENTRY
 [ 0xe139a29c - 0xe12a9ba4 ]
   +0x000 Flink            : 0xe139a29c _LIST_ENTRY [ 0xe12a9ba4 - 0xf78eec78 ]
   +0x004 Blink            : 0xe12a9ba4 _LIST_ENTRY [ 0xf78eec78 - 0xe139a29c ]

第一个元素:

前三个值分别对应结构

typedef struct _MYDATASTRUCT 
{
	ULONG number;
	LIST_ENTRY ListEntry;
}MYDATASTRUCT, *PMYDATASTRUCT;

{
 ULONG number;
 _LIST_ENTRY *Flink;
 _LIST_ENTRY *Blink;
}

kd> dd 0xe139a29c -4
e139a298  00000001 e12a9ba4 f78eec78 87357020
e139a2a8  0c090203 76666647 e139a2b8 00000000
e139a2b8  647ce000 01c2f458 00000000 007d0000
e139a2c8  00001630 e1d1c308 00000001 00000000

第二个元素:

kd> dd 0xe12a9ba4-4
e12a9ba0  00000000 f78eec78 e139a29c 87c816f0
e12a9bb0  0c060403 f7564d43 e115c740 e12aac40
e12a9bc0  e12a9bc0 e12a9bc0 0009c000 00004000
e12a9bd0  dea5c000 884d1831 00000000 05000072


因为是从头部插入链表,所以先插入的元素在链表后面。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
list_entry是一个宏定义,用于获取包含某个list_head指针的结构体的指针。该宏定义如下: ```C #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) ``` 其中,`ptr`是指向`list_head`的指针,`type`是包含`list_head`的结构体的类型,`member`是`list_head`在结构体中的成员名。该宏定义将`ptr`强制转换为`type`类型的结构体指针,并通过计算偏移量将`list_head`的指针转换为结构体的指针。这样就可以通过结构体指针访问到完整的结构体对象了。 举个例子,假设有以下结构体定义: ```C typedef struct _MYDATASTRUCT { ULONG number; LIST_ENTRY ListEntry; } MYDATASTRUCT, *PMYDATASTRUCT; ``` 其中,`MYDATASTRUCT`是包含`LIST_ENTRY`的结构体类型。通过使用`list_entry`宏,可以方便地获取`MYDATASTRUCT`指针。 在`list_entry`宏内部,通过将`ptr`的地址减去`&((type *)0)->member`的地址,可以得到`list_head`相对于包含它的结构体起始地址的偏移量。然后将这个偏移量加上`ptr`的地址,即可得到包含`list_head`的结构体的指针。 对于`MYDATASTRUCT`来说,它的`list_head`成员是`ListEntry`,而在`list_entry`宏的使用中,`member`参数就是`ListEntry`。这样,通过调用`list_entry`宏并传入`ptr`、`MYDATASTRUCT`和`ListEntry`,就可以获取到包含`ptr`的`MYDATASTRUCT`指针了。 需要注意的是,`list_entry`宏中的0是一个虚拟的结构体指针,用于计算偏移量。实际使用时,需要将其替换为实际的结构体类型。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [list_entry的宏定义](https://blog.csdn.net/lhf0921/article/details/5474095)[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_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [LIST_ENTRY链表学习](https://blog.csdn.net/ilovemayverymuch/article/details/38742117)[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_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值