sys/queue.h

概述

        sys/queue.h是LINUX/UNIX系统下面的一个标准头文件,用一系列的数据结构定义了一队列。包括singly-lined list, list, simple queue(Singly-linked Tail queue), tail queue, circle queue五种。

        引用此头文件对这五种数据结构的描述:

A singly-linked list is headed by a single forward pointer. The
elements are singly linked for minimum space and pointer manipulation
overhead at the expense of O(n) removal for arbitrary elements. New
elements can be added to the list after an existing element or at the
head of the list.  Elements being removed from the head of the list
should use the explicit macro for this purpose for optimum
efficiency. A singly-linked list may only be traversed in the forward
direction.  Singly-linked lists are ideal for applications with large
datasets and few or no removals or for implementing a LIFO queue.

A list is headed by a single forward pointer (or an array of forward
pointers for a hash table header). The elements are doubly linked
so that an arbitrary element can be removed without a need to
traverse the list. New elements can be added to the list before
or after an existing element or at the head of the list. A list
may only be traversed in the forward direction.

A simple queue is headed by a pair of pointers, one the head of the
list and the other to the tail of the list. The elements are singly
linked to save space, so elements can only be removed from the
head of the list. New elements can be added to the list after
an existing element, at the head of the list, or at the end of the
list. A simple queue may only be traversed in the forward direction.

A tail queue is headed by a pair of pointers, one to the head of the
list and the other to the tail of the list. The elements are doubly
linked so that an arbitrary element can be removed without a need to
traverse the list. New elements can be added to the list before or
after an existing element, at the head of the list, or at the end of
the list. A tail queue may be traversed in either direction.

A circle queue is headed by a pair of pointers, one to the head of the
list and the other to the tail of the list. The elements are doubly
linked so that an arbitrary element can be removed without a need to
traverse the list. New elements can be added to the list before or after
an existing element, at the head of the list, or at the end of the list.
A circle queue may be traversed in either direction, but has a more
complex end of list detection.

        简单来说,即是单链表,双链表,单链队列,双向队列(尾队列)和双向循环队列。

        虽然这是LINUX/UNIX里面的文件,但此文件本身没有用到LINUX/UNIX的系统特性,因而可以跨平台使用。queue.h下载。

        下面对各数据结构简单描述之。

单链表(singly-linked list)

        singly-linked list就是一单链表。

        singly-linked list相关的定义:

宏定义说明
SLIST_HEAD(name, type)定义表头结点。
name: 表头结点名。
type: 结点类型。
SLIST_HEAD_INITIALIZER(head)初始化头结点。
head: 表头结点。
SLIST_ENTRY(type)定义链表的链域。
type: 结点类型。

        singly-linked list函数:

宏定义说明
SLIST_INIT(head)初始化头结点。
head: 表头结点。
SLIST_INSERT_AFTER(slistelm, elm, field)将结点elm插入到结点slistelm后面。
slistelm:链表中某结点。
elm:要插入的结点。
field:链表中链域的名称。
SLIST_INSERT_HEAD(head, elm, field)将结点elm插入到头结点head后面。
head: 表头结点。
elm:要插入的结点。
field:链表中链域的名称。
SLIST_REMOVE_HEAD(head, field)移除将表头结点下面一个结点。
head: 表头结点。
field:链表中链域的名称。
SLIST_REMOVE(head, elm, type, field)移除将elm结点,elm结点一定要是链表中一结点。
head: 表头结点。
elm:某结点。
type: 结点类型。
field:链表中链域的名称。
SLIST_FOREACH(var, head, field)遍历链表,相当于for循环。
var: 结点类型的变量名称。
head: 表头结点。
field:链表中链域的名称。

        singly-linked list 访问方法:

宏定义说明
SLIST_EMPTY(head)判断链表是否为空。
head: 表头结点。
SLIST_FIRST(head)访问链表里的第一个元素。
head: 表头结点。
SLIST_NEXT(elm, field)访问elm结点后一个元素。
elm:某结点。
field:链表中链域的名称。

        简单例子:

struct SListItem
{int data;SLIST_ENTRY(SListItem) entry;
};
/*struct SListItem{int data;struct {struct SListItem* sle_next;} entry;}*/
void slist_demo()
{struct SListItem* item = NULL;SLIST_HEAD(SListHead, SListItem) shead;/*struct SListHead {struct SListItem* slh_first;} shead;*/SLIST_INIT(&shead);item = (struct SListItem*)malloc(sizeof(struct SListItem));item->data = 1;SLIST_INSERT_HEAD(&shead, item, entry);/*item->entry.sle_next = (&shead)->slh_first;(&shead)->slh_first = item;*/item = (struct SListItem*)malloc(sizeof(struct SListItem));item->data = 2;SLIST_INSERT_HEAD(&shead, item, entry);/*item->entry.sle_next = (&shead)->slh_first;(&shead)->slh_first = item;*/SLIST_FOREACH(item, &shead, entry){printf("%d ", item->data);}/*for(item = (&shead)->slh_first; item; item = item->entry.sle_next){...}*/printf("\n");while(!SLIST_EMPTY(&shead)){item = SLIST_FIRST(&shead);printf("remove %d\n", item->data);SLIST_REMOVE(&shead, item, SListItem, entry);free(item);}/*while(!((&shead)->slh_first == NULL)){item = (&shead)->slh_first;...(&shead)->slh_first = (&shead)->slh_first->entry.sle_next;...}*/
}
/*结果
2 1
remove 2
remove 1
*/

双向链表(list)

        list就是双向链表,不过链域有点古怪,指向前一个结点是指针的指针。

        list 相关定义

宏定义说明
LIST_HEAD(name, type)定义表头结点。
name: 表头结点名。
type: 结点类型。
LIST_HEAD_INITIALIZER(head)初始化头结点。
head: 表头结点。
LIST_ENTRY(type)定义链表的链域。
type: 结点类型。

        list函数

宏定义说明
LIST_INIT(head)初始化头结点。
head: 表头结点。
LIST_INSERT_AFTER(listelm, elm, field)将结点elm插入到结点listelm后面。
listelm:链表中某结点。
elm:要插入的结点。
field:链表中链域的名称。
LIST_INSERT_BEFORE(listelm, elm, field)将结点elm插入到结点listelm前面。
listelm:链表中某结点。
elm:要插入的结点。
field:链表中链域的名称。
LIST_INSERT_HEAD(head, elm, field)将结点elm插入到头结点head后面。
head: 表头结点。
elm:要插入的结点。
field:链表中链域的名称。
LIST_REMOVE(elm, field)移除将elm结点。
elm:某结点。
field:链表中链域的名称。
LIST_FOREACH(var, head, field)遍历链表,相当于for循环。
var: 结点类型的变量名称。
head: 表头结点。
field:链表中链域的名称。

        list访问方法

宏定义说明
LIST_EMPTY(head)判断链表是否为空。
head: 表头结点。
LIST_FIRST(head)访问链表里的第一个元素。
head: 表头结点。
LIST_NEXT(elm, field)访问elm结点后一个元素。
elm:某结点。
field:链表中链域的名称。

        注意,因为list是双向链表,但在访问方法里没有写出访问前一个元素的宏。因而可以这样写一个,参数含义和LIST_NEXT一样:

#define LIST_PRE(elm, field) \
(((elm)->field.le_pre) != &elm ? *((elm)->field.le_pre) : NULL)

        简单例子:

struct ListItem
{int data;LIST_ENTRY(ListItem) entry;
};
/*
struct ListItem
{int data;struct{struct ListItem* le_next;struct ListItem** le_prev;} entry;
};
*/
void list_demo()
{struct ListItem* item = NULL;LIST_HEAD(ListHead, ListItem) lhead;/*struct ListHead {struct ListItem* lh_first;} lhead;*/LIST_INIT(&lhead);/*do{(&lhead)->lh_first = NULL;}while(0);*/item = (struct ListItem*)malloc(sizeof(struct ListItem));item->data = 1;LIST_INSERT_HEAD(&lhead, item, entry);item = (struct ListItem*)malloc(sizeof(struct ListItem));item->data = 2;LIST_INSERT_HEAD(&lhead, item, entry);/*do{if(((item)->entry.le_next = (&lhead)->lh_first) != NULL)(&lhead)->lh_first->entry.le_pre = &(elm)->entry.le_next;(&lhead)->lh_first = (item);(item)->entry.le_prev = &(&lhead)->lh_first;}while(0);*/LIST_FOREACH(item, &lhead, entry){printf("%d ", item->data);}/*for ((item) = ((&lhead)->lh_first);(item);(item) = ((item)->entry.le_next)){...}    */printf("\n");while(!LIST_EMPTY(&lhead)){item = LIST_FIRST(&lhead);printf("remove %d\n", item->data);LIST_REMOVE(item, entry);free(item);}/*while(!((&lhead)->lh_first == NULL)){item = ((&lhead)->lh_first);...do{if ((item)->entry.le_next != NULL)                \(item)->entry.le_next->entry.le_prev =             \(item)->entry.le_prev;                \*(item)->entry.le_prev = (item)->entry.le_next;            \} while (0);...}*/
}
/*
结果
2 1
remove 2
remove 1
*/

简单队列(simple queue)

        简单来说,就是表对有两个链域,分别指向头和尾。

        simple queue 定义(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
SIMPLEQ_HEAD(name, type) 
SIMPLEQ_HEAD_INITIALIZER(head) 
SIMPLEQ_ENTRY(type) 

        simple queue函数(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
SIMPLEQ_INIT(head) 
SIMPLEQ_INSERT_HEAD(head, elm, field) 
SIMPLEQ_INSERT_TAIL(head, elm, field) 
SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) 
SIMPLEQ_REMOVE_HEAD(head, field) 
SIMPLEQ_REMOVE(head, elm, type, field) 
SIMPLEQ_FOREACH(var, head, field) 

        simple queue方法(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
SIMPLEQ_EMPTY(head) 
SIMPLEQ_FIRST(head) 
SIMPLEQ_NEXT(elm, field) 

        简单例子:

        用法与list用法类似,不再重复。

单链尾队列(singled-linked tail queue)

        这个和Simple queue是一样的,参考simple queue

        singled-linked tail queue定义(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
STAILQ_HEAD(name, type) 
STAILQ_HEAD_INITIALIZER(head) 
STAILQ_ENTRY(type) 

       tail queue 函数(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
STAILQ_INIT(head) 
STAILQ_INSERT_HEAD(head, elm, field) 
STAILQ_INSERT_TAIL(head, elm, field) 
STAILQ_INSERT_AFTER(head, listelm, elm, field) 
STAILQ_REMOVE_HEAD(head, field) 
STAILQ_REMOVE(head, elm, type, field) 
STAILQ_FOREACH(var, head, field) 

        tail queue方法(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
STAILQ_EMPTY(head) 
STAILQ_FIRST(head) 
STAILQ_NEXT(elm, field) 

        简单例子:

        用法与list用法类似,不再重复。

循环队列(circle queue)

        循环队列。

        circle queue定义(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
LIST_HEAD(name, type) 
LIST_HEAD_INITIALIZER(head) 
LIST_ENTRY(type) 

        circle queue函数(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
LIST_INIT(head) 
LIST_INSERT_AFTER(listelm, elm, field) 
LIST_INSERT_BEFORE(listelm, elm, field) 
LIST_INSERT_HEAD(head, elm, field) 
LIST_REMOVE(elm, field) 
LIST_FOREACH(var, head, field) 

        circle queue访问方法(具体说明不再写,可以参考list的,或者就直接展开宏)

宏定义说明
LIST_EMPTY(head) 
LIST_FIRST(head) 
LIST_NEXT(elm, field) 

        简单例子

        用法与list用法类似,不再重复。

小结

        虽然这是linux/unix实现的经过长时间考验的成熟的数据结构,但是如果不是很熟悉的话,第一次用起来还是感觉挺不习惯的。但是好在各个数据结构的定义和方法都非常类似,接口比较统一,如果用多的了,熟悉了,感觉就不错了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384933.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sys/queue.h分析(图片复制不过来,查看原文)

这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现,感觉实现的很是优美,关键是以后再也不需要自己实现这些基本的数据结构了,哈哈! 我的系统环境是 正好需要使用队列,那么本篇就以其中的尾队列&…

线程池原理及C语言实现线程池

备注:该线程池源码参考自传直播客培训视频配套资料; 源码:https://pan.baidu.com/s/1zWuoE3q0KT5TUjmPKTb1lw 密码:pp42 引言:线程池是一种多线程处理形式,大多用于高并发服务器上,它能合理有效…

iptables 的mangle表

mangle表的主要功能是根据规则修改数据包的一些标志位,以便其他规则或程序可以利用这种标志对数据包进行过滤或策略路由。 内网的客户机通过Linux主机连入Internet,而Linux主机与Internet连接时有两条线路,它们的网关如图所示。现要求对内网进…

Linux常用命令(一)

history 查看历史命令 ctrlp 向上翻历史纪录 ctrln 向下翻历史纪录 ctrlb 光标向左移动 ctrlf 光标向右移动 ctrla 光标移动到行首 ctrle 光标移动到行尾 ctrlh 删除光标前一个 ctrld 删除光标后一个 ctrlu 删除光标前所有 ctrlL clear命令 清屏 tab键可以补全命令/填充路径…

ip route / ip rule /iptables 配置策略路由

Linux 使用 ip route , ip rule , iptables 配置策略路由 要求192.168.0.100以内的使用 10.0.0.1 网关上网,其他IP使用 20.0.0.1 上网。 首先要在网关服务器上添加一个默认路由,当然这个指向是绝大多数的IP的出口网关。 ip route add default gw 20.0.0.…

iptables:tproxy做透明代理

什么是透明代理 客户端向真实服务器发起连接,代理机冒充服务器与客户端建立连接,并以客户端ip与真实服务器建立连接进行代理转发。因此对于客户端与服务器来说,代理机都是透明的。 如何建立透明代理 本地socket捕获数据包 nat方式 iptables…

编译参数(-D)

程序中可以使用#ifdef来控制输出信息 #include<stdio.h> #define DEBUGint main() {int a 10;int b 20;int sum a b; #ifdef DEBUGprintf("%d %d %d\n",a,b,sum); #endifreturn 0; } 这样在有宏定义DEBGU的时候就会有信息输出 如果注销掉宏定义就不会有输…

libpcap讲解与API接口函数讲解

ibpcap&#xff08;Packet Capture Library&#xff09;&#xff0c;即数据包捕获函数库&#xff0c;是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口&#xff0c;为底层网络监测提供了一个可移植的框架。 一、libpcap工作原理 libpcap…

Linux常用命令(三)

man 查看帮助文档 alias ls : 查看命令是否被封装 echo &#xff1a; 显示字符串到屏幕终端 echo $PATH : 将环境变量打印出来 poweroff&#xff1a;关机 rebot&#xff1a;重启 需要管理员权限 vim是从vi发展过来的文本编辑器 命令模式&#xff1a;打开文件之后默认进入命令模…

浅谈iptables防SYN Flood攻击和CC攻击

何为syn flood攻击&#xff1a; SYN Flood是一种广为人知的DoS&#xff08;拒绝服务攻击&#xff09;是DDoS&#xff08;分布式拒绝服务攻击&#xff09;的方式之一&#xff0c;这是一种利用TCP协议缺陷&#xff0c;发送大量伪造的TCP连接请求&#xff0c;从而使得被攻击方资源…

Linux之静态库

命名规则&#xff1a; lib 库的名字 .a 制作步骤 生成对应.o文件 .c .o 将生成的.o文件打包 ar rcs 静态库的名字&#xff08;libMytest.a&#xff09; 生成的所有的.o 发布和使用静态库&#xff1a; 1&#xff09; 发布静态 2&#xff09; 头文件 文件如下图所示&…

iptables详解和练习

防火墙&#xff0c;其实说白了讲&#xff0c;就是用于实现Linux下访问控制的功能的&#xff0c;它分为硬件的或者软件的防火墙两种。无论是在哪个网络中&#xff0c;防火墙工作的地方一定是在网络的边缘。而我们的任务就是需要去定义到底防火墙如何工作&#xff0c;这就是防火墙…

Linux之动态库

命令规则 lib 名字 .so 制作步骤 1&#xff09;生成与位置无关的代码&#xff08;生成与位置无关的代码&#xff09; 2&#xff09;将.o打包成共享库&#xff08;动态库&#xff09; 发布和使用共享库 动态库运行原理&#xff1a; 生成动态库&#xff1a; gcc -fPIC -c *.c -…

linux下源码安装vsftpd-3.0.2

1&#xff09;在http://vsftpd.beasts.org/网站中查找并下载 vsftpd-3.0.2.tar.gz源码包 2)如果自己的机器上安装有yum可以用yum grouplist | less指令查看以下开发环境&#xff0c;当然这一步不做也行 3&#xff09;拆解源码包 4&#xff09;查看源码包 5&#xff09;编辑…

Linux之GDB调试命令

gdb启动 gdb 程序名 l 查看源代码&#xff08;默认显示十行&#xff09; l 文件名&#xff1a;行数 l 文件名&#xff1a;函数名 添加断点 break 行数 &#xff08;b 也行&#xff09; b 15 if i 15 条件断点 i b 查看断点信息 start 程序执行一步 n 单步调试 s 单步&#xf…

Gdb 调试core文件详解

一&#xff0c;什么是coredump 我们经常听到大家说到程序core掉了&#xff0c;需要定位解决&#xff0c;这里说的大部分是指对应程序由于各种异常或者bug导致在运行过程中异常退出或者中止&#xff0c;并且在满足一定条件下&#xff08;这里为什么说需要满足一定的条件呢&#…

Linux之GDB命令(二)

gdb命令&#xff1a; 前提条件&#xff1a;可执行文件必须包含调试信息 gcc -ggdb 文件名 –启动gdb调试查看代码命令 当前文件&#xff1a; list 行号&#xff08;函数名&#xff09; 指定文件&#xff1a; list 文件名&#xff1a;行号&#xff08;函数名&#x…

Windows下编译openssl库

1、概述 OpenSSL是一个开放源代码的软件库包&#xff0c;它实现了 SSL&#xff08;Secure SocketLayer&#xff09;和 TLS&#xff08;Transport Layer Security&#xff09;协议&#xff0c;所以应用程序可以使用这个包来进行安全通信&#xff0c;避免窃听&#xff0c;同时确…

Makefile规则介绍

Makefile 一个规则 三要素&#xff1a;目标&#xff0c;依赖&#xff0c;命令 目标&#xff1a;依赖命令 1、第一条规则是用来生成终极目标的规则 如果规则中的依赖不存在&#xff0c;向下寻找其他的规则 更新机制&#xff1a;比较的是目标文件和依赖文件的时间 两个函…

windows环境下C语言socket编程

最近由于实验需要&#xff0c;要求写一个c程序与java程序通信的软件&#xff0c;为了测试首先写了一个windows环境下c语言的socket&#xff08;tcp&#xff09;通信程序。 首先socket通信的步骤&#xff1a; 图一 socket通信步骤&#xff08;转载) 图二 三次握手协议&…