TailQueue详解

来源:互联网 发布:域名反向解析 编辑:程序博客网 时间:2024/05/18 05:03

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);          

    }

}

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

0 0
原创粉丝点击