sys/queue.h分析
2016-08-29 11:58
302 查看
from http://blog.csdn.net/astrotycoon/article/details/42917367
这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现,感觉实现的很是优美,关键是以后再也不需要自己实现这些基本的数据结构了,哈哈!
我的系统环境是
正好需要使用队列,那么本篇就以其中的尾队列(tail queue)为例,结合实际的测试程序和示意图(亿图软件)来说明。
测试程序tailq.c如下:
[cpp] view
plain copy
print?
#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>
struct _Data {
int value;
TAILQ_ENTRY(_Data) tailq_entry;
};
int main(int argc, const char *argv[])
{
/* 1. 初始化队列 */
#if 0
TAILQ_HEAD(tailq_head, _Data) head = TAILQ_HEAD_INITIALIZER(head);
#else
TAILQ_HEAD(tailq_head, _Data) head;
TAILQ_INIT(&head);
#endif
int i;
struct _Data *pdata = NULL;
/* 2. 在队列末尾插入data1 */
struct _Data *data1 = (struct _Data *)calloc(1, sizeof(struct _Data));
data1->value = 1;
TAILQ_INSERT_TAIL(&head, data1, tailq_entry);
/* 3. 在队列末尾插入data2 */
struct _Data *data2 = (struct _Data *)calloc(1, sizeof(struct _Data));
data2->value = 2;
TAILQ_INSERT_TAIL(&head, data2, tailq_entry);
/* 4. 在data1之后插入data3 */
struct _Data *data3 = (struct _Data *)calloc(1, sizeof(struct _Data));
data3->value = 3;
TAILQ_INSERT_AFTER(&head, data1, data3, tailq_entry);
/* 5. 在data2之前插入data4 */
struct _Data *data4 = (struct _Data *)calloc(1, sizeof(struct _Data));
data4->value = 4;
TAILQ_INSERT_BEFORE(data2, data4, tailq_entry);
/* 6. 在队列首部插入data5 */
struct _Data *data5 = (struct _Data *)calloc(1, sizeof(struct _Data));
data5->value = 5;
TAILQ_INSERT_HEAD(&head, data5, tailq_entry);
/* 遍历队列 */
TAILQ_FOREACH(pdata, &head, tailq_entry) {
printf("pdata->value1 = %d\n"<
16a62
/span>, pdata->value);
}
puts("");
/* 7. 删除data5 */
TAILQ_REMOVE(&head, data5, tailq_entry);
TAILQ_FOREACH(pdata, &head, tailq_entry) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 正序遍历尾队列 */
/* 方法一 */
TAILQ_FOREACH(pdata, &head, tailq_entry) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 方法二 */
for (pdata = TAILQ_FIRST(&head); pdata;
pdata = TAILQ_NEXT(pdata, tailq_entry)) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 逆序遍历尾队列 */
/* 方法一 */
TAILQ_FOREACH_REVERSE(pdata, &head, tailq_head, tailq_entry) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 方法二 */
for (pdata = TAILQ_LAST(&head, tailq_head); pdata;
pdata = TAILQ_PREV(pdata, tailq_head, tailq_entry)) {
printf("pdata->value1 = %d\n", pdata->value);
TAILQ_REMOVE(&head, pdata, tailq_entry);
free(pdata);
}
if (TAILQ_EMPTY(&head)) {
printf("the tail queue is empty now.\n");
}
exit(EXIT_SUCCESS);
}
我们首先来看一下这个尾队列的定义:
注意,其中的tqe_prev指向的不是前一个元素,而是前一个元素中的tqe_next,这样定义的一个好处就是*tqe_prev就是自身的地址,**tqe_prev就是自身。
好,现在就顺着我的测试程序来一步步看如何使用这个尾队列吧!
第一步是初始化步骤。关于初始化我们有两种方法:使用宏TAILQ_HEAD_INITIALIZER或者使用宏TAILQ_INIT,这两者都是可以的,唯一的区别是传递给宏TAILQ_INIT的是地址,而传递给宏TAILQ_HEAD_INITIALIZER的不是,这点需要引起我们的注意。
初始化后的数据结构怎样的呢? 我们看下示意图:
接下来的两个步骤(步奏2和步奏3)都是在这个队列的尾部追加元素(data1和data2),使用的是宏TAILQ_INSERT_TAIL:
那么队列的变化过程是这样的,请看示意图:
接下来的操作是在data1之前插入data3,使用的是宏TAILQ_INSERT_AFTER:
形象的示意图如下:
整理后的示意图如下:
紧接着的操作是在data2之前插入data4,使用的是宏TAILQ_INSERT_BEFORE:
形象的示意图如下:
整理后的示意图如下:
现在在队列首部插入data5,使用的是宏TAILQ_INSERT_HEAD:
形象的示意图如下:
整理后的示意图如下:
删除数据data5使用是宏TAILQ_REMOVE:
现在的队列布局如下:
好了,基本的操作就这么多,接下来我们看看提供的几个数据元素访问方法:
前三个很简单,一看就懂,我们重点分析下TAILQ_LAST和TAILQ_PREV。
TAILQ_LAST的目的是获取队列中的最后一个元素的地址,注意是地址哦!(head)->tqh_last代表的是最后一个元素中tqe_next的地址,通过强转之后,((struct headname *)((head)->tqh_last))->tqh_last实际上就是最后一个元素中的tqe_prev,而文章一开始介绍定义的时候就说过,*tqe_prev代表的是自身元素的地址,所以TAILQ_LAST最后获取的就是最后一个元素的地址,宏TAILQ_PREV的道理是一样的。
OK,测试程序接下来就是遍历整个队列,并打印出数据,可以使用提供的宏TAILQ_FOREACH,也可以使用上述的几个访问方法来遍历。
好了,其实本文没啥内容,对我个人而言就主要是想熟悉下亿图这个软件,哈哈
参考链接:
《queue.h之尾队列》
《关于libevent与FreeBSD内核中TAILQ队列的理解》
《深入理解FreeBSD中的TAILQ》
《libevent源码分析-- queue.h中TAILQ_QUEUE的理解》
《queue.h(Circular queue 循环队列) 》
《libevent源码之TAILQ详解》
《 QEMU代码中的QLIST》
《Libevent源码分析-----TAILQ_QUEUE队列》
这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现,感觉实现的很是优美,关键是以后再也不需要自己实现这些基本的数据结构了,哈哈!
我的系统环境是
正好需要使用队列,那么本篇就以其中的尾队列(tail queue)为例,结合实际的测试程序和示意图(亿图软件)来说明。
测试程序tailq.c如下:
[cpp] view
plain copy
print?
#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>
struct _Data {
int value;
TAILQ_ENTRY(_Data) tailq_entry;
};
int main(int argc, const char *argv[])
{
/* 1. 初始化队列 */
#if 0
TAILQ_HEAD(tailq_head, _Data) head = TAILQ_HEAD_INITIALIZER(head);
#else
TAILQ_HEAD(tailq_head, _Data) head;
TAILQ_INIT(&head);
#endif
int i;
struct _Data *pdata = NULL;
/* 2. 在队列末尾插入data1 */
struct _Data *data1 = (struct _Data *)calloc(1, sizeof(struct _Data));
data1->value = 1;
TAILQ_INSERT_TAIL(&head, data1, tailq_entry);
/* 3. 在队列末尾插入data2 */
struct _Data *data2 = (struct _Data *)calloc(1, sizeof(struct _Data));
data2->value = 2;
TAILQ_INSERT_TAIL(&head, data2, tailq_entry);
/* 4. 在data1之后插入data3 */
struct _Data *data3 = (struct _Data *)calloc(1, sizeof(struct _Data));
data3->value = 3;
TAILQ_INSERT_AFTER(&head, data1, data3, tailq_entry);
/* 5. 在data2之前插入data4 */
struct _Data *data4 = (struct _Data *)calloc(1, sizeof(struct _Data));
data4->value = 4;
TAILQ_INSERT_BEFORE(data2, data4, tailq_entry);
/* 6. 在队列首部插入data5 */
struct _Data *data5 = (struct _Data *)calloc(1, sizeof(struct _Data));
data5->value = 5;
TAILQ_INSERT_HEAD(&head, data5, tailq_entry);
/* 遍历队列 */
TAILQ_FOREACH(pdata, &head, tailq_entry) {
printf("pdata->value1 = %d\n"<
16a62
/span>, pdata->value);
}
puts("");
/* 7. 删除data5 */
TAILQ_REMOVE(&head, data5, tailq_entry);
TAILQ_FOREACH(pdata, &head, tailq_entry) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 正序遍历尾队列 */
/* 方法一 */
TAILQ_FOREACH(pdata, &head, tailq_entry) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 方法二 */
for (pdata = TAILQ_FIRST(&head); pdata;
pdata = TAILQ_NEXT(pdata, tailq_entry)) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 逆序遍历尾队列 */
/* 方法一 */
TAILQ_FOREACH_REVERSE(pdata, &head, tailq_head, tailq_entry) {
printf("pdata->value1 = %d\n", pdata->value);
}
puts("");
/* 方法二 */
for (pdata = TAILQ_LAST(&head, tailq_head); pdata;
pdata = TAILQ_PREV(pdata, tailq_head, tailq_entry)) {
printf("pdata->value1 = %d\n", pdata->value);
TAILQ_REMOVE(&head, pdata, tailq_entry);
free(pdata);
}
if (TAILQ_EMPTY(&head)) {
printf("the tail queue is empty now.\n");
}
exit(EXIT_SUCCESS);
}
我们首先来看一下这个尾队列的定义:
注意,其中的tqe_prev指向的不是前一个元素,而是前一个元素中的tqe_next,这样定义的一个好处就是*tqe_prev就是自身的地址,**tqe_prev就是自身。
好,现在就顺着我的测试程序来一步步看如何使用这个尾队列吧!
第一步是初始化步骤。关于初始化我们有两种方法:使用宏TAILQ_HEAD_INITIALIZER或者使用宏TAILQ_INIT,这两者都是可以的,唯一的区别是传递给宏TAILQ_INIT的是地址,而传递给宏TAILQ_HEAD_INITIALIZER的不是,这点需要引起我们的注意。
初始化后的数据结构怎样的呢? 我们看下示意图:
接下来的两个步骤(步奏2和步奏3)都是在这个队列的尾部追加元素(data1和data2),使用的是宏TAILQ_INSERT_TAIL:
那么队列的变化过程是这样的,请看示意图:
接下来的操作是在data1之前插入data3,使用的是宏TAILQ_INSERT_AFTER:
形象的示意图如下:
整理后的示意图如下:
紧接着的操作是在data2之前插入data4,使用的是宏TAILQ_INSERT_BEFORE:
形象的示意图如下:
整理后的示意图如下:
现在在队列首部插入data5,使用的是宏TAILQ_INSERT_HEAD:
形象的示意图如下:
整理后的示意图如下:
删除数据data5使用是宏TAILQ_REMOVE:
现在的队列布局如下:
好了,基本的操作就这么多,接下来我们看看提供的几个数据元素访问方法:
前三个很简单,一看就懂,我们重点分析下TAILQ_LAST和TAILQ_PREV。
TAILQ_LAST的目的是获取队列中的最后一个元素的地址,注意是地址哦!(head)->tqh_last代表的是最后一个元素中tqe_next的地址,通过强转之后,((struct headname *)((head)->tqh_last))->tqh_last实际上就是最后一个元素中的tqe_prev,而文章一开始介绍定义的时候就说过,*tqe_prev代表的是自身元素的地址,所以TAILQ_LAST最后获取的就是最后一个元素的地址,宏TAILQ_PREV的道理是一样的。
OK,测试程序接下来就是遍历整个队列,并打印出数据,可以使用提供的宏TAILQ_FOREACH,也可以使用上述的几个访问方法来遍历。
好了,其实本文没啥内容,对我个人而言就主要是想熟悉下亿图这个软件,哈哈
参考链接:
《queue.h之尾队列》
《关于libevent与FreeBSD内核中TAILQ队列的理解》
《深入理解FreeBSD中的TAILQ》
《libevent源码分析-- queue.h中TAILQ_QUEUE的理解》
《queue.h(Circular queue 循环队列) 》
《libevent源码之TAILQ详解》
《 QEMU代码中的QLIST》
《Libevent源码分析-----TAILQ_QUEUE队列》
相关文章推荐
- Android BLE设备蓝牙通信框架BluetoothKit
- UIPickerView实现年月选择器
- requestAnimationFrame
- Hibernate的session.createSQLQuery的几种查询方式
- sys/queue.h
- iOS 使用AFNetworking遇到异常 Request failed: unacceptable content-type: text/html
- UGUI 技术点笔记 (干货走起)
- java学习—探秘Java中的String、StringBuilder以及StringBuffer
- 基于MVC+EasyUI的web开发框架之使用云打印控件C-Lodop打印页面或套打报关运单信息
- jquey怎么判断滚动条滚动到了底部(这是对前面图片墙的改良)
- android uevent
- Count Numbers with Unique Digits
- [转载]Macaca 测试 Android 应用:UIAutomator
- queue.h的链表使用
- UE4 HTC Menu Map
- 如何在UIScrollView的xib上添加子控件并添加AutoLayout约束
- Longest Common Subsequence 最长公共子序列
- #34 N-Queens II
- 读《About Face 4 交互设计精髓》16
- UILabel,文字添加下划线,中划线