C/C++技巧
+ -

C/C++技巧 巧用链表

2023-06-06 22 1

链表经常是使用的。
比如说我们通常定义如下的结构体:

typedef struct _DATA_STRUCT
{
     PDATA_STRUCT pNext;         //下一个链表指针
    static PDATA_STRUCT pFirst  //第一个链表指针

    //其它成员变量
    ...
}DATA_STRUCT,*PDATA_STRUCT

DATA_STRUCT:: pFirst =  NULL;

在使用过程中,我们直接使用 DATA_STRUCT:: pFirst获取第一个链表指针,通过其成员变量pNext获取下一个链接指针,直接下一个pNext== NULL时,链表枚举完毕。

for(PDATA_STRUCT pData =DATA_STRUCT:: pFirst;pData->Next != NULL;pData = pData->Next)
{
    //do somethign
    ...
}

这种情况下在一般的情况下使用是没有任何问题的,但假如我们定义的这个结构体是用于写文件或者网络发送,这个有点尴尬了,因为这个结构体有一个与我们数据毫不相关的成员变量pNext,这样在每次使用这个数据流时,总是要特别的处理,甚是难用。

静态成员变量pFist不占结构体内存,相当于全局变量,存放于全局可读可写数据区。

那么有没有好一点的方法呢?
答案是有的,比如就有人说,我可以把数据重新弄一个结构体,放在这个DATA_STRUCT的前面声名,每次取时进行强转换成实际数据结构体,再用sizeof取结构体大小。
例如:

typedef struct _REAL_DATA
{
    //其它成员变量
}REAL_DATA,*PREAL_DATA;

typedef struct _DATA_STRUCT
{
    REAL_DATA;

     PDATA_STRUCT pNext;         //下一个链表指针
    static PDATA_STRUCT pFirst  //第一个链表指针

}DATA_STRUCT,*PDATA_STRUCT
DATA_STRUCT:: pFirst =  NULL;

...

PDATA_STRUCT pDataStruct  = //获取其结构体指针
PREAL_DATA pRealData = (PREAL_DATA)pDataStruct;
int  nSize = sizeof(REAL_DATA);

这样使用没有任何毛病,但有没有更加彻底的方法了?
我们可以这样使用:定义一个CONTAINING_RECORD宏,自动计算2个结构体之间的偏移,并获取其需要的结构体的指针.

定义的这个宏 CONTAINING_RECORD 用到了 [结构体偏移] 的知识,并进行了深化改造。

// t.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#define CONTAINING_RECORD(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field))

typedef struct _LIST_STRUCT
{
    _LIST_STRUCT* pNext;
    _LIST_STRUCT* pPre;
} LIST_STRUCT, *PLIST_STRUCT;

typedef struct _MY_STRUCT
{
    int a;
    int b;
    LIST_STRUCT list;
} MY_STRUCT, *PMY_STRUCT;

PLIST_STRUCT  GeListStruct()
{
    PLIST_STRUCT pListStruct= NULL;

    //do some thing

    return pListStruct;
}

void main(int argc, _TCHAR* argv[])
{
    PLIST_STRUCT  pList = GeListStruct();
    PMY_STRUCT pMy = CONTAINING_RECORD(pList, MY_STRUCT, list);

}

在windows 驱动开发时,ddk定义了一个结构体LIST_ENTRY,其就是使用了这样的技巧。

typedef struct _LIST_ENTRY {
  struct _LIST_ENTRY *Flink;
  struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;

0 篇笔记 写笔记

链表LIST_ENTRY
Windows的内核开发者们自己开发了部分数据结构,比如说LIST_ENTRY。LIST_ENTRY是一个双向链表结构。它总是在使用的时候,被插入到已有的数据结构中。下面举一个例子。我构筑一个链表,这个链表的每个节点,是一个文件名和一个文件大小两个数据成员组成的结构。此外有一个FILE_OBJEC......
WDM 驱动中创建的设备链表
一个驱动加载后,可以根据需要创建多个设备,这些设备会以链表的形式连接起来,并且第一个设备的指针存放在DRIVER_OJECT的DeviceObject成员中。后续的设备会依次按DEVICE_OJBECT的NextDevice进行链表连接,直到最后一个为NULL.3: kd> dt _DEVI......
C/C++技巧 巧用链表
链表经常是使用的。比如说我们通常定义如下的结构体:typedef struct _DATA_STRUCT{ PDATA_STRUCT pNext; //下一个链表指针 static PDATA_STRUCT pFirst //第一个链表指针 //其它......
C/C++技巧 自动链表结构体
我们知道MFC为了加速消息处理,内部实现了消息的管理和派发。但当消息产生时,是通过类的实例链表自动时行偿试处理。那么组成类的链表是怎么自动实现的?我们知道,全局变量或者类的静态成员是在main函数之前初始化,其构造函数优先于main函数的调用。所以当我们需要自动进行类的实例链表的时候,可以给类添加静......
C/C++技巧 自动链表
使用C++的构造函数实现链表的自动链接我们知道MFC为了加速消息处理,内部实现了消息的管理和派发。但当消息产生时,是通过类的实例链表自动时行偿试处理。那么组成类的链表是怎么实现的?我们知道,全局变量或者类的静态成员是在main函数之前初始化,其构造函数优先于main函数的调用。所以当我们需要自动进行......
线程等待链表/调度链表
线程3种状态:正在运行就绪等待等待线程链表阻塞的线程在等待线程链表中。KTHREAD结构体中: +0x0d8 WaitListEntry : _LIST_ENTRY +0x0d8 SwapListEntry : _SINGLE_LIST_ENTRY当一个线程处于等待状......
Windows内核中的链表LIST_ENTRY与结构体
以下文章虽为转载,但也包含了本人的修改与测试验证。Windows内核中是无法使用vector容器等数据结构的,当我们需要保存一个结构体数组时,就需要使用内核中提供的专用链表结构LIST_ENTRY通过一些列链表操作函数对结构体进行装入弹出等操作,LIST_ENTRY是Windows内核中定义的......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!