循环队列多用于通信数据缓存中,尤其是在双方设备接收数据与处理数据不同步的情况下,使用循环队列先缓存通信数据,然后按照时间戳数据出队作出相应的处理,是一种比较合适的做法,在嵌入式编程中亦是如此。使用循环队列的数据结构可以实现上述功能,在一些低端的编程平台手写一个循环队列既满足了功能需求又不会开销太多资源。

设计思想

实现一个队列可以使用顺序表(数组)或链表实现。前者访问速度块,但是要占用连续的存储空间,适用于内存小但是速度要求较快的存储场合。后者访问速度慢但是基于链表式的结构,可以使用碎片内存,适用内存大,速度慢的场合。本文面向的编程平台是中低端MCU,时钟主频、RAM空间有限,因此选用顺序表来实现循环队列。

关于顺序表实现循环队列的文章网上又很多,但是大多都基于一个明确的数据类型,如果在一个工程中两种完全不同的数据类型都想使用循环队列的数据结构就会使得相同的代码出现多次,导致代码冗余。

泛类型循环队列的思想是将顺序表中每个节点都看作一个uint8_t*类型的指针,在队列初始化时,要传入节点占用空间字节数,对每个节点malloc一个相应的存储空间,当数据入队时使用memcpy函数将源地址字节数拷贝到目标地址即可,队列数据表的结构有点类似于哈希表,操作与定类型循环队列类似。

代码实现

队列实现包含两个文件:queue.h和queue.c

queue.h:

使用枚举自定义BOOL类型,C99标准之后包含stdbool.h可使用标准布尔型

 typedef enum
{
FALSE,
TRUE
} BOOL;

宏定义顺序表中的节点类型

#define NODTETYPE uint8_t*

定义循环队列结构体

typedef struct Queue
{
uint32_t capacity; // 队列总容量
uint32_t size; // 当前队列大小
uint32_t front; // 队列头节点
uint32_t rear; // 队列尾节点
NODTETYPE* data; //存储节点的顺序表
} Queue;

接口函数

/* 初始化一个队列 */
BOOL init_queue(Queue *queue,uint32_t _capacity,uint32_t DataWidth);
/* 数据入队 */
BOOL en_queue(Queue* _queue, NODTETYPE _data,uint32_t DataWidth);
/*队列判空*/
BOOL isempty_queue(Queue* _queue);
/*队列判满*/
BOOL isfull_queue(Queue* _queue);
/* 数据出队 */
NODTETYPE de_queue(Queue* _queue);
/* 清空队列 */
void clear_queue(Queue* _queue);

queue.c

队列初始化,此处必须传入节点的数据占用字节数DataWidth,注意可用队列容量总是比传入参数_capacity小一,因为要判空和判满。

BOOL init_queue(Queue *queue,uint32_t _capacity,uint32_t DataWidth)
{
NODTETYPE *buff= (NODTETYPE*)malloc(_capacity*sizeof(NODTETYPE));
if(buff==NULL)
return FALSE;
for(int i=0;i<_capacity;i++)
{
NODTETYPE NodeTemp=(NODTETYPE)malloc(DataWidth);
if(NodeTemp==NULL)
return FALSE;
else
buff[i]=NodeTemp;
}
queue->data=buff;
queue->capacity = _capacity;
queue->size = 0;
queue->front = 0;
queue->rear = 0;
return TRUE;
}

数据节点入队

BOOL en_queue(Queue* _queue, NODTETYPE _data,uint32_t DataWidth)
{
BOOL isFull;
isFull = isfull_queue(_queue);
if (isFull == TRUE)
{
_queue->front = (_queue->front + 1) % _queue->capacity;
}
memcpy(_queue->data[_queue->rear], _data,DataWidth);
_queue->rear = (_queue->rear + 1) % _queue->capacity;
_queue->size++;
return isFull;
}

数据节点出队

NODTETYPE de_queue(Queue* _queue)
{
if (isempty_queue(_queue))
return NULL;
NODTETYPE result = _queue->data[_queue->front];
_queue->front = (_queue->front + 1) % _queue->capacity;
_queue->size--;
return result;
}

队列清空,但是不释放存储空间

void clear_queue(Queue* _queue)
{
_queue->front = _queue->rear = 0;
_queue->size = 0;
}

队列判空

BOOL isempty_queue(Queue* _queue)
{
if (_queue->front == _queue->rear)
return TRUE;
else
return FALSE;
}

队列判满

BOOL isfull_queue(Queue* _queue)
{
if ((_queue->rear + 1) % _queue->capacity == _queue->front)
return TRUE;
else
return FALSE;
}

队列清空并释放内存

void release_queue(Queue* _queue)
{
for(int i=0;i<_queue->capacity;i++)
{
free(_queue->data[i]);
_queue->data[i]=NULL;
}
clear_queue(_queue);
free(_queue);
_queue = NULL;
}

调用时入队时,要先将节点数据类型强制转化为uint8_t类型,传入形参,出队时获取一个uint8_t的指针,然后强制转换为定义的节点类型指针,之后就可以访问到出队节点的数据,举例如下:

#include "queue.h"
typedef struct ClassTest //定义测试类型
{
uint8_t a;
uint16_t b;
uint32_t c;
}ClassTest;
int main(int argc, char *argv[])
{
Queue queue1;
Queue queue2;
init_queue(&queue1,100,sizeof (ClassTest));
init_queue(&queue2,200,1); //测试队列2就用uint8_t
int i=0;
uint8_t queue1_node=0;
ClassTest queue2_node={0,0,0};
while(isfull_queue(&queue1)==FALSE)
{
queue1_node=i++;
en_queue(&queue1,&queue1_node,sizeof (queue1_node));
}
i=0;
while(isfull_queue(&queue2)==FALSE)
{
queue2_node.a=i++;
queue2_node.b=2*i;
en_queue(&queue2,(uint8_t*)(&queue2_node),sizeof (queue2_node));
}
while(isempty_queue(&queue1)==FALSE)
{
NODTETYPE node=de_queue(&queue1);
printf("%d;",*((uint8_t*)(node)));
}
while(isempty_queue(&queue2)==FALSE)
{
NODTETYPE node=de_queue(&queue2);
prinf("%d;",((ClassTest*)(node))->b);
}
}

基于C语言的泛类型循环队列的更多相关文章

  1. C语言数据结构-循环队列的实现-初始化、销毁、清空、长度、队列头元素、插入、删除、显示操作

    1.数据结构-循环队列的实现-C语言 #define MAXSIZE 100 //循环队列的存储结构 typedef struct { int* base; //基地址 int _front; //头 ...

  2. 数据结构算法C语言实现(十二)--- 3.4循环队列&队列的顺序表示和实现

    一.简述 空队列的处理方法:1.另设一个标志位以区别队列是空还是满:2.少用一个元素空间,约定以队列头指针在队尾指针下一位置上作为队列呈满的状态的标志. 二.头文件 //3_4_part1.h /** ...

  3. Atitit.提升软件稳定性---基于数据库实现的持久化 循环队列 环形队列

    Atitit.提升软件稳定性---基于数据库实现的持久化  循环队列 环形队列 1. 前言::选型(马) 1 2. 实现java.util.queue接口 1 3. 当前指针的2个实现方式 1 1.1 ...

  4. C语言中不同类型的循环(Different types of loops in C)

    C语言中有三种类型的循环:for,while,do-while. while循环先判断循环条件. while (condition) { //gets executed after condition ...

  5. Atitit.升级软件的稳定性---基于数据库实现持久化 循环队列 循环队列

    Atitit.升级软件的稳定性---基于数据库实现持久化  循环队列 环形队列 1. 前言::选型(马) 1 2. 实现java.util.queue接口 1 3. 当前指针的2个实现方式 1 1.1 ...

  6. C语言——循环队列和链队列的基本运算

    // 循环队列#include <stdio.h> #include "SeqQue.h" // 循环队列的基本运算 /* const int maxsize = 20 ...

  7. C语言实现使用动态数组实现循环队列

    我在上一篇博客<C语言实现使用静态数组实现循环队列>中实现了使用静态数组来模拟队列的操作. 因为数组的大小已经被指定.无法动态的扩展. 所以在这篇博客中,我换成动态数组来实现. 动态数组能 ...

  8. 数据结构(C语言版)---第三章栈和队列 3.4.2 队列的链式表示和实现(循环队列)

    这个是循环队列的实现,至于串及数组这两章,等有空再看,下面将学习树. 源码如下: #include <stdio.h> #include <stdlib.h> #define ...

  9. c语言编程之循环队列

    利用链表实现的循环队列,完成了队列的入队和出队,对于队空和队满用了一个flag进行标记.入队flag++,出队flag-- #include"stdio.h" typedef in ...

  10. 数据结构:循环队列(C语言实现)

    生活中有非常多队列的影子,比方打饭排队,买火车票排队问题等,能够说与时间相关的问题,一般都会涉及到队列问题:从生活中,能够抽象出队列的概念,队列就是一个能够实现"先进先出"的存储结 ...

随机推荐

  1. 基于docker和cri-dockerd部署k8sv1.26.3

    cri-dockerd是什么? 在 Kubernetes v1.24 及更早版本中,我们使用docker作为容器引擎在k8s上使用时,依赖一个dockershim的内置k8s组件:k8s v1.24发 ...

  2. 用 Go 剑指 Offer 11. 旋转数组的最小数字

    已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:若旋转 4 次,则可以得到 [ ...

  3. [Java]排序算法>交换排序>【快速排序】(O(N*logN)/不稳定/N较大/无序/仅顺序存储)

    1 快速排序 1.1 算法思想 快速排序是由冒泡排序改进而得的. 在冒泡排序过程中,只对相邻的2个记录进行比较:因此,每次交换2个相邻记录时,只能消除1个逆序. 若能通过2个(不相邻)记录的1次交换, ...

  4. kali linux 基本渗透测试流程

    渗透测试流程 1. 信息收集阶段 网络拓扑结构分析 使用nmap扫描目标网络,获取目标主机IP地址和开放端口信息 使用whois查询目标域名的注册信息和DNS服务器信息 使用nslookup查询目标域 ...

  5. Laravel 代码开发最佳实践(持续更新)

    我们这里要讨论的并不是 Laravel 版的 SOLID 原则(想要了解更多 SOLID 原则细节查看这篇文章)亦或是设计模式,而是 Laravel 实际开发中容易被忽略的最佳实践. 内容概览 单一职 ...

  6. P1350 车的放置 题解

    一.题目描述: 给你一个网格棋盘,a,b,c,d 表示了对应边长度,也就是对应格子数. 例如,当 a=b=c=d=2 时,对应了下面这样一个棋盘: 想要在这个棋盘上放 k 棋子,也就是这 k 个棋子没 ...

  7. Centos7.x 安装jenkins

    一.安装 前提:需查看是否安装了JDK 1.第一种方法 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat ...

  8. Spring源码:bean的生命周期(一)

    前言 本节将正式介绍Spring源码细节,将讲解Bean生命周期.请注意,虽然我们不希望过于繁琐地理解Spring源码,但也不要认为Spring源码很简单.在本节中,我们将主要讲解Spring 5.3 ...

  9. 【Redis】-使用Lua脚本解决多线程下的超卖问题以及为什么?

    一.多线程下引起的超卖问题呈现1.1.我先初始化库存数量为1.订单数量为0 1.2.开启3个线程去执行业务 业务为:判断如果说库存数量大于0,则库存减1,订单数量加1 结果为:库存为-2,订单数量为3 ...

  10. 18年CCCC赛后总结

    C4赛后总结: 我正式入坑以来,大约5个月,这也是我第一次出去参与这样正式的比赛,其实比赛结果并不尽人意,但有很多还是需要记录下来的,通过这次比赛的确获得了很多的比赛经验: 一赛前: 其实赛前的状态, ...