5、C语言队列与应用

队列

掌握队列的原理和作用
掌握队列的写法
掌握队列在产品中的应用

队列的原理
队列原理其实就像一个管道,如果我们不断往管道里面塞乒乓球,每个乒乓球在管道里就会排列一条队形。先进去的乒乓球就会先出来,这个就是队列的先进先出的规则。
在这里插入图片描述
球从左边进去,进去的动作就是入列,然后进去的球在管道里排成一个队列,这个叫队列缓存,说白了就是数组,那么这里存了5个球就相当于是buff[5];这样的意思,最右边出来的1号球就是最早进去的球,出来的这个动作叫出列,所以遵循了先进先出的规则。
队列的作用
队列最主要的作用就是用来缓存数据,比方说串口接收数据,我们一般定义一个数组来存储数据,但是假如串口数据频率很快,可能这个数组里存储的数据还没处理完,下一组串口数据又过来了,那么这时候数组里的数据就会被新数据覆盖,导致老的数据丢失。像这种就可以通过队列的方式来处理,每收到一个字节数据都先入列,然后应用程序同步解析处理,根据队列先进先出的规则,那么老的数据就不会被新的数据“插队”了。
基于这种缓存数据的技术,可以灵活应用在各种场景,比如说:
1、操作系统的消息传递
2、大数据处理
队列程序设计思路
其实实现队列的方法有很多种,但是工作原理都是一样的,我们要编写代码,首先要很清楚队列的工作原理。
1、队列缓存
2、入列
3、出列
一个队列基本上要有上面这三个必要的操作,那么队列缓存就很好理解,说白了就是直接定义一个数组,数组大小就是队列缓存的大小。入列就是把1个或者若干个数据按顺序存到队列缓存数组里,同样出列把数据从队列缓存里取出来。
入列和出列的原理懂了,那么应该怎么样用程序来实现呢?
入列
根据上文的说法,入列就是把数据存进数组的操作,我们平时存数组一般都是buff[0]=1;这样操作。不过在队列中我们要考虑队列里当前存在多少个数据的情况,如果有数据,那么我们就不能从[0]这个下标开始入列,所以我们在入列时要考虑两个问题:
1、队列缓存可以存储的数组下标位置,这个我们一般称为队尾
2、队列是否已满,如果队列缓存满了又有新的数据入列,该怎么处理?这里我们一般处理方式是按照时间顺序,把最早入列的数据丢弃,以新的数据替换。
第二个问题先暂时不管,先看第一个问题。根据前面学过的数组与指针,通过指针的特性,我们在用1个指针来代表队尾,然后这个队尾指针指向队列缓存数组的首地址,当入列1个数据时,我们的队尾指针就+1,这样是不是就能够知道当前队列缓存的可以存储的地址了?
在这里插入图片描述
出列
数据入列以后自然要取出来,那么我们取的时候也是有原则的,不能乱取,而是从最早入列那个数据的地址开始取,所以这个出列的数组下标我们称为队头,同样我们可以使用指针变量来代表这个队头。
我们看看下面这个图是一个出列的流程,我们这个是满编队的队列,总共有1,2,3,4,5个数据,那么队头指针指向队列缓存首地址,接着第一个出列的就是数据1,出列后队头指针加1,就指向数据2的地址,那么数据2出列后,队头指针又加1,指向数据3的地址,以此类推,这样就可以实现先进先出的原则。
在这里插入图片描述
掌握队列程序编写
那么我们知道原理后就可以用程序来实现它了,因为一个产品中,会有很多队列代表不同的数据,比如说消息我有一个队列,底层串口接收我也有一个队列,所以我们吧队列的一些操作,比如说入列,出列的操作单独写成函数接口,方便程序不同区域的调用。
main.c代码

#include <stdio.h>
#include "Queue.h"
Queue4 KeyMsg;

int main(int argc, char *argv[])
{
	unsigned char a; 
	QueueEmpty(KeyMsg);/*初始化*/
	printf("site:%d\r\n",sizeof(KeyMsg.Buff)); 
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("&buff[0]=0x%x, &buff[1]=0x%x, &buff[2]=0x%x, &buff[3]=0x%x \r\n",&KeyMsg.Buff[0],&KeyMsg.Buff[1],&KeyMsg.Buff[2],&KeyMsg.Buff[3]);
	printf("\r\n"); 
	
	a = 1;
	QueueDataIn(KeyMsg,&a,1);/*要用哪一个队列,要存那个数据,要存多少个数据*/
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("buff[0]=0x%x, buff[1]=0x%x, buff[2]=0x%x, buff[3]=0x%x \r\n",KeyMsg.Buff[0],KeyMsg.Buff[1],KeyMsg.Buff[2],KeyMsg.Buff[3]);
	printf("\r\n"); 
	
	a = 2;
	QueueDataIn(KeyMsg,&a,1);
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("buff[0]=0x%x, buff[1]=0x%x, buff[2]=0x%x, buff[3]=0x%x \r\n",KeyMsg.Buff[0],KeyMsg.Buff[1],KeyMsg.Buff[2],KeyMsg.Buff[3]);
	printf("\r\n"); 
	
	a = 3;
	QueueDataIn(KeyMsg,&a,1);
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("buff[0]=0x%x, buff[1]=0x%x, buff[2]=0x%x, buff[3]=0x%x \r\n",KeyMsg.Buff[0],KeyMsg.Buff[1],KeyMsg.Buff[2],KeyMsg.Buff[3]);
	printf("\r\n"); 
	
    a = 4;
	QueueDataIn(KeyMsg,&a,1);
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("buff[0]=0x%x, buff[1]=0x%x, buff[2]=0x%x, buff[3]=0x%x \r\n",KeyMsg.Buff[0],KeyMsg.Buff[1],KeyMsg.Buff[2],KeyMsg.Buff[3]);
	printf("\r\n"); 
	
	a = 5;
	QueueDataIn(KeyMsg,&a,1);
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("buff[0]=0x%x, buff[1]=0x%x, buff[2]=0x%x, buff[3]=0x%x \r\n",KeyMsg.Buff[0],KeyMsg.Buff[1],KeyMsg.Buff[2],KeyMsg.Buff[3]);
	printf("\r\n"); 
	
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	QueueDataOut(KeyMsg,&a);/*要用哪一个队列,要取那个数据*/
	printf("a=%d\r\n",a); 
	printf("&KeyMsg=0x%x, KeyMsg.Buff=0x%x, KeyMsg.Head=0x%x, KeyMsg.Tail=0x%x\r\n",&KeyMsg,KeyMsg.Buff,KeyMsg.Head,KeyMsg.Tail);
	printf("buff[0]=0x%x, buff[1]=0x%x, buff[2]=0x%x, buff[3]=0x%x \r\n",KeyMsg.Buff[0],KeyMsg.Buff[1],KeyMsg.Buff[2],KeyMsg.Buff[3]);
	printf("\r\n"); 
	
	return 0;
}

queue.c代码

void S_QueueEmpty(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff)
{
	*Head = pBuff;
	*Tail = pBuff; 
}

void S_QueueDataIn(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff,unsigned char Len,unsigned char *pData,unsigned char DataLen)
{
	unsigned char num;
	//关闭中断 
	for(num=0; num < DataLen; num++,pData++)
	{	
		**Tail = *pData;		//这里把数据入列 
		(*Tail)++;				//队尾指针加1
	 	if(*Tail == pBuff+Len)
	 	{
	 		*Tail = pBuff; 
	 	}
		if(*Tail == *Head)
		{
			
			if(++(*Head) == pBuff+Len)
			{
				*Head = pBuff;	
			}
		}
	}
	//打开中断 
}

unsigned char S_QueueDataOut(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff,unsigned char Len,unsigned char *pData)
{
	unsigned char status;
	//关闭中断 
	status = 0;
	if(*Head != *Tail)
	{
		*pData = **Head;
		status = 1;
		if(++(*Head) == pBuff+Len)
		{
			*Head = pBuff;
		}
	}
	//恢复中断 
	return status;
}

unsigned short S_QueueDataLen(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff,unsigned char Len)
{
	if(*Tail > *Head)
	{
		return *Tail-*Head;
	}
	if(*Tail < *Head)
	{
		return *Tail+Len-*Head;	
	}
}

queue.h代码

#ifndef _QUEUE_H_
#define _QUEUE_H_

extern void S_QueueEmpty(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff);
extern void S_QueueDataIn(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff,unsigned char Len,unsigned char *pData,unsigned char DataLen);
extern unsigned char S_QueueDataOut(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff,unsigned char Len,unsigned char *pData); 
extern unsigned short S_QueueDataLen(unsigned char **Head,unsigned char **Tail,unsigned char *pBuff,unsigned char Len); 

#define QueueEmpty(x)  S_QueueEmpty((unsigned char **)&(x).Head,(unsigned char **)&(x).Tail,(unsigned char *)(x).Buff)

#define QueueDataIn(x,y,z) S_QueueDataIn((unsigned char **)&(x).Head,(unsigned char **)&(x).Tail,(unsigned char *)&(x).Buff,sizeof((x).Buff),y,z)
#define QueueDataOut(x,y)  S_QueueDataOut((unsigned char **)&(x).Head,(unsigned char **)&(x).Tail,(unsigned char *)&(x).Buff,sizeof((x).Buff),y)
#define QueueDataLen(x) 	S_QueueDataLen((unsigned char **)&(x).Head,(unsigned char **)&(x).Tail,(unsigned char *)&(x).Buff,sizeof((x).Buff))
 
typedef struct
{
	unsigned char *Head; 	//队头指针,用来出列用的 
	unsigned char *Tail; 	//队尾指针,用来入列用的 
	unsigned char Buff[4];	//队列缓存 
}Queue4;

typedef struct
{
	unsigned char *Head; 	//队头指针,用来出列用的 
	unsigned char *Tail; 	//队尾指针,用来入列用的 
	unsigned char Buff[128];	//队列缓存 
}Queue128;

typedef struct
{
	unsigned char *Head; 	//队头指针,用来出列用的 
	unsigned char *Tail; 	//队尾指针,用来入列用的 
	unsigned char Buff[512];	//队列缓存 
}Queue512;
#endif

队列的应用
串口的应用
在这里插入图片描述
在这里插入图片描述
如果产品有两个功能,一个功能需要灯一秒闪1次,另一个功能需要灯1秒闪2次,在功能切换很快的情况下,需要功能正常并且灯的闪烁正常,那么就需要队列了。
本文博客主要参考无际单片机编程,https://b23.tv/PXO5IMQ。

#include #include #include //队列最大长度 #define MAX_QUEUE 1024 //偷懒,就用静态队列了 static int mQueue[MAX_QUEUE]; //队列插入 void InsertData(int **Front, int **Rear) { if (*Rear + 1 == *Front && (*Rear + 1 - MAX_QUEUE != *Front)) { //当队列数据已满,返回 puts("Queue Size Overflow!\n"); return; } else if (*Rear - mQueue > MAX_QUEUE) { //实现的是类似循环队列,但由于是静态线性队列(数组) //而不是用链表来实现的,所以到静态队列(数组)尾部,尾指针自动指向(数组)头部 *Rear = mQueue; } puts("Input Data:"); scanf("%d", *Rear); //输入数据后,尾指针后移 *Rear += 1; } //从头指针删除一个队列中的数据 void DeleteData(int **Front, int **Rear) { if (*Front == *Rear) { //头指针尾指针重合,队列空,不能删除,返回 puts("Queue Empty!\n"); return; } else if (*Front - mQueue > MAX_QUEUE) { //参考 Rear *Front = mQueue; } //从头指针删除一个数据 *Front += 1; } //显示队列数据 void ShowData(int **Front, int **Rear) { int *temp; for (temp=*Front; temp!=*Rear; temp++) { printf("%d --> ", *temp); } puts("\n"); } void usage(void) { puts("1. Insert Data"); puts("2. Delete Data"); puts("3. Show Data"); } int main(int argc, char **argv) { //头指针,尾指针 //队列的一个特性 First in first out FIFO int *pFront, *pRear; int op_code; //初始化队列,头指针和尾指针此时指向的地址相同 pFront = pRear = mQueue; while (1) { usage(); scanf("%d", &op_code); switch (op_code) { case 1: printf("%p\n", pFront); printf("%d\n", *pFront); InsertData(&pFront, &pRear); break; case 2: DeleteData(&pFront, &pRear); break; case 3: ShowData(&pFront, &pRear); break; default: break; } } return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值