C/C++技巧 巧用链表
2023-06-06
24
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;