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

设计思想

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

    前言 <腾讯云 x Elasticsearch三周年>活动来了.文章写之前的思路是:在腾讯云服务器使用docker搭建ES.但是理想很丰满,显示很骨感,在操作过程中一波三折,最后还是含着泪 ...

  2. 四月十一号Java基础知识

    1.下列格式调用JAVA语言定义的方法:字符串变量名.方法名():2.由键盘输入多个数据普通格式一:Scanner reader= new Scanner(System.in): int number ...

  3. Hydra详细使用

    1. 简介 Hydra是什么 Hydra是什么: Hydra是一款网络登录破解工具,可以通过暴力破解方式来猜解用户名和密码,从而获取系统的访问权限.它可以支持多种协议,如FTP.SSH.Telnet. ...

  4. DG:windows密码文件

    问题描述:搭建DG,找不到密码文件的位置,就给备库重新生成了一个密码文件,传到了备库,但是拉到了备库以后,恢复过程中,trace日志在报错,后来才知道windows下的密码文件跟linux平台下的面文 ...

  5. Nvidia GPU热迁移-Singularity

    1 背景 在GPU虚拟化和池化的加持下,可以显著提高集群的GPU利用率,同时也可以较好地实现弹性伸缩.但有时会遇到需要GPU资源再分配的场景,此时亟需集群拥有GPU任务热迁移的能力.举个简单的例子,比 ...

  6. Python程序笔记20230304

    抛硬币实验 random 模块 import random random.randint(a, b) 返回一个随机整数 N,范围是:a <= N <= b random.choice(&q ...

  7. Spring @Profile注解使用和源码解析

    介绍 在之前的文章中,写了一篇使用Spring @Profile实现开发环境,测试环境,生产环境的切换,之前的文章是使用SpringBoot项目搭建,实现了不同环境数据源的切换,在我们实际开发中,会分 ...

  8. 文件上传漏洞靶场:upload-labs(附在线地址)

    重装系统:CentOS 7.6 密钥对验证,或密码验证,根据自身情况选择,博主这边为了ssh连接方便选用的密码校验. WindTerm登录系统 需提前去云服务器的安全组,开放22端口ssh连接. 更新 ...

  9. 【总结】从++i思考计算机原子性和线程安全

    在C++中,++i被认为是一种原子性操作,即不可分割的.不可中断的整体.它能够确保对变量的修改完整且正确,从而避免了数据竞争等问题,提高了程序的并发性和可靠性.然而,有些人可能会将原子性和线程安全混淆 ...

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

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