C/C++技巧 巧用链表

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

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;
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

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

Powered by bytekits.com,汇天下文字,成非凡梦想!!!