看源代码的时候发现一个新的数据结构,然后我就好好的画了一下这个数据结构的图形,一是为了记录一下我自己的分析过程,二是和大家分享一下这个数据结构。这个数据结构的名字叫TAIL QUEUE感觉很高大上。所以我想分析一下这个数据结构,这个数据结构里面使用到了很多的宏定义。
这个结构体的定义如下:
typedef struct ConfNode_ { char *name; char *val; int is_seq; int allow_override; struct ConfNode_ *parent; TAILQ_HEAD(, ConfNode_) head; TAILQ_ENTRY(ConfNode_) next; } ConfNode;
ConfNode结构的图形是这个样子:
TAIL_HEAD又是什么呢?这是一个宏定义,结构如下:
#define TAILQ_HEAD(name, type) struct name { struct type *tqh_first; /* first element */ struct type **tqh_last; /* addr of last next element */ }
里面包含了一个两个成员,一个是指向type类型的tqh_first指针,一个是tqh_last,这个是指向指针的指针。这个type就是传过来的ConfNode_,说明这个类型和之前的声明的ConfNode的类型是一样的。里面的注释也写的很清楚,tqh_first指向第一个元素,tqh_last是指向next element的地址。
TAIL_ENTRY这个宏定义如下:
#define TAILQ_ENTRY(type) struct { struct type *tqe_next; /* next element */ struct type **tqe_prev; /* address of previous next element */ }
同样,这个的解释和上面那个是一样的,没有不同,解释是tqe_next指向的是下一个结点,tqe_prev指向的是上一个元素的地址。
通过替换现在ConfNode结点的代码就如下:
typedef struct ConfNode_ { char *name; char *val; int is_seq; int allow_override; struct ConfNode_ *parent; struct name { struct type *tqh_first; /* first element */ struct type **tqh_last; /* addr of last next element */ } head; struct { struct type *tqe_next; /* next element */ struct type **tqe_prev; /* address of previous next element */ } next; } ConfNode;
现在的结构图变成这个样子了:
接下来就是申明结点的操作:
ConfNode * ConfNodeNew(void) { ConfNode *new; new = SCCalloc(1, sizeof(*new)); if (unlikely(new == NULL)) { return NULL; } /* By default we allow an override. */ new->allow_override = 1; TAILQ_INIT(&new->head); return new; }
这里面主要是TAILQ_INIT这个宏的定义解释:
#define TAILQ_INIT(head) do { (head)->tqh_first = NULL; (head)->tqh_last = &(head)->tqh_first; } while (0)
这里主要是将刚分配的ConfNode结点元素里面的head变量里的tqh_first和tqh_last进行初始化。
这里的tqh_first初始化为NULL,tqh_last初始化为指向tqh_first,也就是说tqh_last里面出入的是tqh_first的地址。
接下来就是准备插入元素的过程了,插入元素使用的是TAILQ_INSERT_TAIL这个宏定义,在这之前肯定是先声明一个root结点的。
<pre name="code" class="cpp">ConfNode *parent = NULL;
ConfNode *node = parent;
刚开始插入的时候,由于这两个结点都是NULL,还没有分配任何空间,所以肯定是不行的,在里面有地方是给它分配空间的,这个代码就是这里:
node = ConfNodeNew();
因为node和parent指向的是同一个地方,所以开始插入的时候比较特殊。
TAILQ_INSERT_TAIL(&parent->head, node, next);
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)
呀呀呀,这个东西看上去有点绕了,那我就用画图的形式来解释吧。其实它做的工作也就那么点并没有很复杂。刚开始分配空间的时候tqh_first和tqh_last是初始化过的,还记得吧,tqh_last指向tqh_first。
第一步刚开始初始化的情况如下:
接下来执行第一步:
(elm)->field.tqe_next = NULL;
接下来就是指向下面的代码:
(elm)->field.tqe_prev = (head)->tqh_last;
第一次插入的时候注意的是elm和head其实是同一个结点,所以都是对这个结点进行操作,这个执行的意思是tqe_prev和tqh_last是指向同一个地方的,tqh_last我们知道,指向的是tqh_first。所以tqe_prev也是指向tqh_first。
接下来的两行代码执行的其实就是修改了里面的指针:
*(head)->tqh_last = (elm); (head)->tqh_last = &(elm)->field.tqe_next;
第一行代码我们知道tqh_last指向的是tqh_first的地址,所以操作*(head)->tqh_last其实就是操作的tqh_first。而这个tqh_first = NULL,所以第一行代码就是修改tqh_first的值,使得tqh_first指向这个结构体的开始。
最后一步就是修改tqh_last的值了,使这个值指向之后一个结点,但这个时候里面的结点只有一个,所以tqh_last指向的是tqe_next的地址。
最后我将画一下多个结点的情况,这个也就是在里面修改一下指针而已。图示如下:
如果看不清楚请原谅,我已经很注意了。如果有什么问题请大家提出来,希望大家讲讲这个数据机构的优点,我是有点没有想明白,双向循环链表也是可以实现的啊。
版权声明:本文为博主原创文章,未经博主允许不得转载。