libevent(4): 最新版的libevent的数据结构queue

RT.


当我看libevent源代码的时候,看到了queue,并且在evbuffer.c里面大量使用,必须先将queue这个关攻克,才可以继续无障碍的看evbuffer.c,乃至bufferevent.h等具体的包装类实现。刚开始学习,免不了要在网上搜索代码了。

这不,刚刚就在csdn搜索到一篇很好的讲述queue用法的文章(地址:http://blog.csdn.net/jiayp004/article/details/13023027)

我按照他给的代码一步一步走,还是容易懂的。唯一让我纠结的就是指针的指针,很麻烦。不得已,我通过debug,并记录下,每个指针的指针变量的地址。来判断。

首先引用一下那篇博客的内容。


// Begin -- TailQueue详解

TailQueue详解

 

本文详细解释Libevent中TailQueue数据结构及使用方法。

采用的基础数据结构如下所示:该数据结构包含2个业务字段,设备编号devId和设备名称name。

struct AlarmInfo
{
 //业务数据
 int devId;
 const char name[32]; 
}

如果要采用TailQueue进行改结构体保存,必须在该数据结构中增加字段用于建立链表之用,如下所示:

struct AlarmInfo
{
 //业务数据
 int devId;
 const char name[32]; 
 TAILQ_ENTRY(AlarmInfo)    TQueueField;
}

TAILQ_ENTRY(AlarmInfo)TQueueField;是一个宏定义,展开后该数据结构如下:

 struct AlarmInfo
{
 //业务数据
 int devId;
 const char name[32]; 
 Struct 
  {
struct AlarmInfo *tqe_next;
struct AlarmInfo **tqe_prev;
  } TQueueField;
}

tqe_next指向下一个要素的地址;

tqe_prev指向前一个要素的next指针的地址。

这样,基本的数据结构就定义完毕。

但是,还必须有其他的辅助手段,才能实现数据的存取。

首先,定义一个列表头,其宏定义如下:                                        

#define TAILQ_HEAD(name, type)
struct name 
{
/* first element */
Struct type *tqh_first;
/* addr of last next element */
Struct type **tqh_last;
}      

对于本文中的AlarmInfo数据需要通过如下语句定义一个新的结构体: TAILQ_HEAD(AlarmInfoTaileQueue,AlarmInfo);

该宏定义展开后如下图所示:

struct AlarmInfoTaileQueue 
{
   Struct AlarmInfo *tqh_first;
   Struct AlarmInfo **tqh_last;
};

 

然后就可以用该结构体定义一个实际的结构体实例,用于保存改队列的头节点。

struct AlarmInfoTaileQueue alarmTQHead;

 

在使用该头节点前,需要对其进行初始化:

 TAILQ_INIT(&alarmTQHead);

其宏定义如下:

#define TAILQ_INIT(head) do {\
(head)->tqh_first = NULL; \
(Head)->tqh_last = &(head)->tqh_first;\
} while (0)

主要作用就是将头指针(tqh_first)初始化为NULL,将尾指针(指向指针的指针)指向头指针(tqh_first)的地址。

接下来,就可以通过该头节点对该队列进行操作了。

//增加要素进入队列

AlarmInfoalr;

for(i=0;i<10;i++)

  {

       //创建该结构体实例。

        alr =NewAlarmInfo();

        if(NULL== alr){return;} 

        alr -> devId =i;      

        sprintf(tem,"jiayp%d",i+1);

        strcpy(alr->name,tem);

        TAILQ_INSERT_TAIL(&alarmTQHead,alr,TQueueField);

   }

TAILQ_INSERT_TAIL宏定义如下:

#define TAILQ_INSERT_TAIL(head, elm, field) do {\
(elm)->field.tqe_next = NULL;\
           (elm)->field.tqe_prev = (head)->tqh_last;\
*(head)->tqh_last = (elm);\
(head)->tqh_last = &(elm)->field.tqe_next;\
} while (0

该宏定义实现功能如下图:

调用TAILQ_INIT(&alarmTQHead)后如下图所示:

 

第一次调用TAILQ_INSERT_TAIL(&alarmTQHead,alr,TQueueField);后如下图所示:

首先,将AlarmInfo节点下TequeueField的tqe_next的指针设置为NULL;

然后,将AlarmInfo节点下TequeueField的tqe_prev(指向指针的指针)设置到alarmTQHead的Tqh_first(因为原来尾指针(指向指针的指针)就是指向头指针的地址);

然后,将尾指针(此时改指针仍然指向头指针的地址),所以是将头指针指向实际的数据对象(AlarmInfo)地址;

最后,将尾指针,指向新对象的teq_next指针的地址。

 

第二次调用TAILQ_INSERT_TAIL(&alarmTQHead,alr,TQueueField);后如下图所示:

 

这样就形成了一个链表,该链表的alarmTQHead节点,分别指向链表的第一和最后一个节点。

下面看一下链表的遍历过程:

static void OutputAll(){

     struct AlarmInfo*alr;

    TAILQ_FOREACH (alr,& alarmTQHead,TQueueField)

    {

        printf(alr->name);      

 }  

}

TAILQ_FOREACH也是一个宏定义:

#define TAILQ_FOREACH(var, head, field)\
for((var) = AILQ_FIRST(head);\
   (var) != TAILQ_END(head);\
   (var) = TAILQ_NEXT(var, field))

代码展开后如下所示:

static void OutputAll()

{

    struct AlarmInfo *alr;  

    for((var) = alarmTQHead ->tqh_first;(var) != NULL; (var)= var->TQueueField. tqe_next)

    {

            printf(alr->name);          

    }

}

该代码简单,不做详细说明。


// End -- TailQueue详解


看完这篇博客,相信大家都明白了差不多了。

下面我给出我调试,并将宏展开后的代码,睁大眼睛哦。。

// 该数据结构包含2个业务字段,设备编号devId和设备名称name。

struct AlarmInfo
{
    //业务数据
    int devId;
    char name[32];

    //TAILQ_ENTRY(AlarmInfo)    TQueueField;
    struct {
        struct AlarmInfo* tqe_next;
        struct AlarmInfo** tqe_prev;
    } TQueueField;
    /* 
    * 展开为:
    * struct {
    *  struct AlarmInfo *tqe_next;	    // next element
    *  struct AlarmInfo **tqe_prev;	// address of previous next element
    *  } TQueueField;
    */
};


//TAILQ_HEAD(AlarmInfoTailQueue, AlarmInfo);
struct AlarmInfoTailQueue {
    struct AlarmInfo *tqh_first;	// first element
    struct AlarmInfo **tqh_last;	// addr of last next element
};
/* 定义了一个结构体
* 展开为: 
* struct AlarmInfoTailQueue {
*  struct AlarmInfo *tqh_first;	// first element
*  struct AlarmInfo **tqh_last;	// addr of last next element
* }
*/


///


inline void Test_Tail_Queue()
{
    struct AlarmInfoTailQueue alarmTQHead;
    //TAILQ_INIT(&alarmTQHead);
    do {
        (&alarmTQHead)->tqh_first = NULL;

        (&alarmTQHead)->tqh_last = &((&alarmTQHead)->tqh_first);
    } while(0);
    // debug
    LogPrintflnA("alarmTQHead: %x", &alarmTQHead);
    LogPrintflnA("tqh_first: %x", &(&alarmTQHead)->tqh_first);
    LogPrintflnA("tqh_last: %x", &(&alarmTQHead)->tqh_last);
    /*
    * 展开为: 
    * do {
    *  (&alarmTQHead)->tqh_first = NULL;
    *  (&alarmTQHead)->tqh_last = &(&alarmTQHead)->tqh_first;
    * } while (0);
    */

    //增加元素进入队列
    char temp[32];
    for( int i=0; i<2; i++) {
        //创建该结构体实例
        AlarmInfo* pInfo = new AlarmInfo;
        // debug
        LogPrintflnA("pInfo: %x", &pInfo);
        LogPrintflnA("&devId: %x", &(pInfo->devId));
        LogPrintflnA("tqe_next: %x", &(pInfo->TQueueField.tqe_next));
        LogPrintflnA("tqh_last: %x", &(pInfo->TQueueField.tqe_prev));

        if( NULL == pInfo ) {
            return;
        }

        sprintf(temp, "jiayp%d", i+1);

        pInfo->devId = i;
        strcpy(pInfo->name, temp);

        do {
            pInfo->TQueueField.tqe_next = NULL;
            pInfo->TQueueField.tqe_prev = (&alarmTQHead)->tqh_last;
            *(&alarmTQHead)->tqh_last = pInfo;
            (&alarmTQHead)->tqh_last = &(pInfo->TQueueField.tqe_next);
        } while(0);
        //TAILQ_INSERT_TAIL(&alarmTQHead, pInfo, TQueueField);
        /*
        * 展开为: 
        * do {
        *   pInfo->TQueueField.tqe_next = NULL;
        *   pInfo->TQueueField.tqe_prev = (&alarmTQHead)->tqh_last;
        *   *(&alarmTQHead)->tqh_last = pInfo;
        *   (&alarmTQHead)->tqh_last = &(pInfo->TQueueField.tqe_next);
        * } while(0);
        */
    }

    // output
    struct AlarmInfo*alr;
    TAILQ_FOREACH(alr, &alarmTQHead, TQueueField)
    {
        LogPrintflnA(alr->name);
    }
}
当我们定义了
struct AlarmInfoTailQueue alarmTQHead;
然后在循环里面,动态创建了一个AlarmInfo,此时他们在内存的布局为:


详见,初始化部分:

经过我们的

        do {
            pInfo->TQueueField.tqe_next = NULL;
            pInfo->TQueueField.tqe_prev = (&alarmTQHead)->tqh_last;
            *(&alarmTQHead)->tqh_last = pInfo;
            (&alarmTQHead)->tqh_last = &(pInfo->TQueueField.tqe_next);
        } while(0);
部分后,其中内存的布局如我们的“插入第一个AlarmInfo'结构所示。


然后我们继续插入第二个AlarmInfo结构,其中内存布局为:



所以,使用TailQueue可以直接索引到第一个和最后一个AlarmInfo。

每一个AlarmInfo之间都是有链表连接的,最后一个AlarmInfo的prev域存放的是一个 AlaramInfoTailQueue的地址。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值