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

设计思想

实现一个队列可以使用顺序表(数组)或链表实现。前者访问速度块,但是要占用连续的存储空间,适用于内存小但是速度要求较快的存储场合。后者访问速度慢但是基于链表式的结构,可以使用碎片内存,适用内存大,速度慢的场合。本文面向的编程平台是中低端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. Linux 磁盘空间查看及清理

    1. 查看磁盘空间 查看当前目录各文件夹大小 du -ah -x --max-depth=1 查看文件大小 ls -lh 查看系统空间占用 df -h 2. 磁盘空间清理 Linux清除文件内容 ca ...

  2. PVE Cloud-INIT 模板配置

    PVE Cloud-INIT 模板配置 Cloud-init是什么 Cloud-init是开源的云初始化程序,能够对新创建弹性云服务器中指定的自定义信息(主机名.密钥和用户数据等)进行初始化配置.通过 ...

  3. 迁移学习《Pseudo-Label : The Simple and Efficient Semi-Supervised Learning Method for Deep Neural Networks》

    论文信息 论文标题:Pseudo-Label : The Simple and Efficient Semi-Supervised Learning Method for Deep Neural Ne ...

  4. 基于SqlSugar的开发框架循序渐进介绍(26)-- 实现本地上传、FTP上传、阿里云OSS上传三者合一处理

    在前面介绍的随笔<基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式[Options]处理常规上传和FTP文件上传>中介绍过在文件上传处理的过程中,整合了本 ...

  5. 基于【ESLint+JavaScript Standard Style】标准的VUE/JS/html风格指南

    小仙男前端代码风格规范指南v1.0 概述 本规范是适用于小仙男团队及前端团队所搭建的各种前端框架代码的通用风格规范指南: 使用时,请遵循指南细则进行代码风格约束,并在提交之前确保进行代码风格的修正操作 ...

  6. Python 使用QQ 邮箱进行发送邮件及经验总结

    今天我带大家实现下简单的发邮件功能.我们要使用到email和smtplib库,这两个库时python自带的,直接import 引用就好了,实现环境python3.6. 对QQ邮箱进行开启SMTP服务 ...

  7. Java双向链表实现队列

    将双向链表做简单的改造,即可实现一个FIFO(First Input First Out)队列, 该队列只在头节点出队,尾节点入队. 一般来说定义节点类只需一个后驱节点next即可. 这里保留pre节 ...

  8. Django 如何使用 Celery 完成异步任务或定时任务

    以前版本的 Celery 需要一个单独的库(django-celery)才能与 Django 一起工作, 但从 Celery 3.1 开始,情况便不再如此,我们可以直接通过 Celery 库来完成在 ...

  9. linux syslog.d日记操作记录-小节

    以下记录在学习LDD3时调试处理打印的一些操作 syslog 不同的发行版,不同的脚本文件,如fedora18中为rsyslog的名称 1:配置文件 /etc/syslog.conf(fedora r ...

  10. VS2022使用ClickOnce发布程序本地安装.net框架

    因为遇到下面的错误,没有在网上搜到详细解决问题的教程,费了一些时间才解决了问题,特此记录一下,也希望能帮助到其他人. 要在"系统必备"对话框中启用"从与我的应用程序相同的 ...