7.2.3 使用双向链表排序的例子
本节定义一个简单的链表,并使用ngx_queue_sort方法对所有元素排序。在这个例子中,可以看到如何定义、初始化ngx_queue_t容器,如何定义任意类型的链表元素,如何遍历链表,如何自定义排序方法并执行排序。
首先,定义链表元素的结构体,如下面的TestNode结构体:
typedef struct{
u_char*str;
ngx_queue_t qEle;
int num;
}TestNode;
链表元素结构体中必须包含ngx_queue_t类型的成员,当然它可以在任意的位置上。本例中它的上面有一个char*指针,下面有一个整型成员num,这样是允许的。
排序方法需要自定义。下面以TestNode结构体中的num成员作为排序依据,实现compTestNode方法作为排序过程中任意两元素间的比较方法。
ngx_int_t compTestNode(const ngx_queue_ta,const ngx_queue_tb)
{
/首先使用ngx_queue_data方法由ngx_queue_t变量获取元素结构体TestNode的地址/
TestNode*aNode=ngx_queue_data(a,TestNode,qEle);
TestNode*bNode=ngx_queue_data(b,TestNode,qEle);
//返回num成员的比较结果
return aNode->num>bNode->num;
}
这个比较方法结合ngx_queue_sort方法可以把链表中的元素按照num的大小以升序排列。在此例中,可以看到ngx_queue_data的用法,即可以根据链表元素结构体TestNode中的qEle成员地址换算出TestNode结构体变量的地址,这是面向过程的C语言编写的ngx_queue_t链表之所以能够通用化的关键。下面来看一下ngx_queue_data的定义:
define ngx_queue_data(q,type,link)\
(type)((u_char)q-offsetof(type,link))
在4.2.2节中曾经提到过offsetof函数是如何实现的,即它会返回link成员在type结构体中的偏移量。例如,在上例中,可以通过ngx_queue_t类型的指针减去qEle相对于TestNode的地址偏移量,得到TestNode结构体的地址。
下面开始定义双向链表容器queueContainer,并将其初始化为空链表,如下所示。
ngx_queue_t queueContainer;
ngx_queue_init(&queueContainer);
链表容器以ngx_queue_t定义即可。注意,对于表示链表容器的ngx_queue_t结构体,必须调用ngx_queue_init进行初始化。
ngx_queue_t双向链表是完全不负责分配内存的,每一个链表元素必须自己管理自己所占用的内存。因此,本例在进程栈中定义了5个TestNode结构体作为链表元素,并把它们的num成员初始化为0、1、2、3、4,如下所示。
int i=0;
TestNode node[5];
for(;i<5;i++)
{
node[i].num=i;
}
下面把这5个TestNode结构体添加到queueContainer链表中,注意,这里同时使用了ngx_queue_insert_tail、ngx_queue_insert_head、ngx_queue_insert_after 3个添加方法,读者不妨思考一下链表中元素的顺序是什么样的。
ngx_queue_insert_tail(&queueContainer,&node[0].qEle);
ngx_queue_insert_head(&queueContainer,&node[1].qEle);
ngx_queue_insert_tail(&queueContainer,&node[2].qEle);
ngx_queue_insert_after(&queueContainer,&node[3].qEle);
ngx_queue_insert_tail(&queueContainer,&node[4].qEle);
根据表7-1中介绍的方法可以得出,如果此时的链表元素顺序以num成员标识,那么应该是这样的:3、1、0、2、4。如果有疑问,不妨写个遍历链表的程序检验一下顺序是否如此。下面就根据表7-1中的方法说明编写一段简单的遍历链表的程序。
ngx_queue_t*q;
for(q=ngx_queue_head(&queueContainer);
q!=ngx_queue_sentinel(&queueContainer);
q=ngx_queue_next(q))
{
TestNode*eleNode=ngx_queue_data(q,TestNode,qEle);
//处理当前的链表元素eleNode
……
}
上面这段程序将会依次从链表头部遍历到尾部。反向遍历也很简单。读者可以尝试使用ngx_queue_last和ngx_queue_prev方法编写相关代码。
下面开始执行排序,代码如下所示。
ngx_queue_sort(&queueContainer,compTestNode);
这样,链表中的元素就会以0、1、2、3、4(num成员的值)的升序排列了。
表7-1中列出的其他方法就不在这里一一举例了,使用方法非常相似。