进阶循环队列

background0001.jpg

大家好,好久不见,没更新的日子里都在躺平,从几年前的精通C语言,到现在的只是熟悉C语言,对C越来越不熟悉了,最近有用到循环队列,本文不再介绍简单的实现队列,而是进阶的循环队列,废话不多说。

队列是什么

队列就是FIFO(First Input First Output)的一种线性表,当然也可以通过链表进行实现。允许从一端放入数据,从另一端取出数据。

001.png

上图中展示了普通队列的方式,本文主要讲解循环队列,如果不理解本文的实现方式,可以从网上查看其他博主写的循环队列实现方式,网上一般主要是一个front和一个rear,当入队时,rear指针向尾部移动,front指针则依旧指向首元素,当出队时,front指针向下一个元素移动,释放出队元素,尾指针不变。

但是在实际的应用中,并不适用,比如系统中的IO读写需要读写循环队列,来一个写的命令,当资源不足的时候可以挂到写队列中,系统从队头取写命令,归根结底就是提高性能,所以队列用链表实现就比较拉胯,不推荐。

要注意的是本文实现方式队头队尾都是自加,当队列中有数据的时候,input数值比output数值大。

本文将使用上文提到的类似方式实现,本质上也是用线性表实现循环队列,一种带头的循环队列,欢迎大家一起交流,本文代码已经经过简单的测试,如有错误欢迎指正,一起共同学习。

002.png

循环队列结构定义

 typedef struct _FIFO_{
     u32_t input;      //input 
     u32_t output; //output
     u32_t length;      //fifo length
     u32_t element_size;    //data size
     u64_t address;   // fifo start address
 }fifo_t;

常用操作

初始化

 /**
  * @brief init fifo 
  * @param fifo pointer , fifo len , per variable size in fifo
  * @return SUCCESS/FAILURE
  */
 u32_t FIFO_INIT(fifo_t* fifo, u32_t length, u32_t element_size){
     //intialize variables
     fifo->input = 0;
     fifo->output = 0;
     fifo->element_size = element_size;
     //save fifo head address
     fifo->address = ((u64_t)fifo) + sizeof(fifo_t);
     //This defines that the queue capacity can only be a power of two.
     if(!(is_power_of_two(length)) || length < 2){
         fifo->length = 0;
         return FAILURE;
     }
     fifo->length = length - 1;
     memset((void*)fifo->address, 0, length * element_size);
     return SUCCESS;
 }

入队

 /**
  * @brief input data to fifo
  * @param fifo pointer , data , len
  * @return  len
  */
 u32_t FIFO_INPUT(fifo_t* fifo, const void* data, u32_t length){
     u32_t balance_fifo_num = FIFO_UNUSED(fifo) ;
     if(!balance_fifo_num)
         return FAILURE;
     if(length > balance_fifo_num)
         length = balance_fifo_num;
     FIFO_COPY(fifo, data, length, fifo->input, _INPUT);
     fifo->input += length;
     return length;
 }

出队

 /**
  * @brief output data to buff
  * @param fifo pointer , buf , len
  * @return 
  */
 u32_t FIFO_OUTPUT(fifo_t* fifo, void* buf , u32_t length){
     u32_t used_fifo_num = FIFO_USED(fifo);
     if(!used_fifo_num)
         return FAILURE;
     if(length > used_fifo_num)
         length = used_fifo_num;
     FIFO_COPY(fifo, buf, length, fifo->output, _OUTPUT);
     fifo->output += length;
     return length;
 }

队列剩余空间

 /**
  * @brief Free space in queue
  * @param fifo pointer
  * @return number
  */
 u32_t FIFO_UNUSED(fifo_t* fifo){
     return ((fifo->length + 1) - (fifo->input - fifo->output));
 }

队列已使用空间

 /**
  * @brief Used space in queue
  * @param fifo pointer
  * @return number
  */
 u32_t FIFO_USED(fifo_t* fifo){
     return (fifo->input - fifo->output);
 }

获取头结点

 /**
  * @brief get queue head
  * @param fifo pointer
  * @return head address
  */
 void* FIFO_GET_HEAD(fifo_t* fifo){
     // fifo->address
     u32_t offset = 0;
     offset &= fifo->length;
     offset *= fifo->element_size;
     return (void*)fifo->address + offset;
 }

结

本文介绍了一种循环队列的实现方式,使用此种方式的主要原因就是提高性能,而不是简单的理解循环队列,在底层系统中,资源比较紧张,循环队列是一种很好的结构,性能高,资源固定,实现也很简单。

最后很抱歉没有在代码中进行注释,阅读可能比较吃力。

附全部代码

Makefile

 CC = gcc
 CFLAGS = -I.
 DEPS = fifo.h
 OBJ = main.o fifo.o 
 
 %.o: %.c $(DEPS)
 $(CC) -c -o $@ $< $(CFLAGS)
 
 fifo: $(OBJ)
 $(CC) -o $@ $^ $(CFLAGS)
 
 .PHONY: clean
 
 clean:
 rm -f fifo *.o

fifo.h

 /****************************************************************
   *@file       : fifo.h
   *@description: 
   *@time       : 2023/08/21 20:26:50
   *@author     : Nick Xia
   *@blog       : https://aeneag.xyz
 *****************************************************************/
 #ifndef _FIFO_H
 #define _FIFO_H
 
 #include <memory.h>
 typedef unsigned char u8_t;
 typedef unsigned short u16_t;
 typedef unsigned int u32_t;
 typedef unsigned long long u64_t;
 
 typedef struct _FIFO_{
     u32_t input;
     u32_t output;
     u32_t length;
     u32_t element_size;
     u64_t address;
 }fifo_t;
 
 #define is_power_of_two(x) ((x) != 0 && (((x) & ((x)-1)) == 0))
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
 #define fifo_total_size(length, element_size) ((length)*(element_size)+(sizeof(fifo_t)))
 
 #define SUCCESS 0
 #define FAILURE 1
 #define _INPUT 0
 #define _OUTPUT 1
 
 u32_t FIFO_UNUSED(fifo_t* fifo);
 u32_t FIFO_USED(fifo_t* fifo);
 u32_t FIFO_INIT(fifo_t* fifo, u32_t length, u32_t element_size);
 u32_t FIFO_INPUT(fifo_t* fifo, const void* data, u32_t length);
 u32_t FIFO_OUTPUT(fifo_t* fifo, void* data, u32_t length);
 void* FIFO_GET_HEAD(fifo_t* fifo);
 
 #endif // !_FIFO_H

fifo.c

 /****************************************************************
   *@file       : fifo.c
   *@description: 
   *@time       : 2023/08/21 20:27:06
   *@author     : Nick Xia
   *@blog       : https://aeneag.xyz
 *****************************************************************/
 #include "fifo.h"
 
 /**
  * @brief init fifo 
  * @param fifo pointer , fifo len , per variable size in fifo
  * @return SUCCESS/FAILURE
  */
 u32_t FIFO_INIT(fifo_t* fifo, u32_t length, u32_t element_size){
     //intialize variables
     fifo->input = 0;
     fifo->output = 0;
     fifo->element_size = element_size;
     //save fifo head address
     fifo->address = ((u64_t)fifo) + sizeof(fifo_t);
     //This defines that the queue capacity can only be a power of two.
     if(!(is_power_of_two(length)) || length < 2){
         fifo->length = 0;
         return FAILURE;
     }
     fifo->length = length - 1;
     memset((void*)fifo->address, 0, length * element_size);
     return SUCCESS;
 }
 /**
  * @brief Free space in queue
  * @param fifo pointer
  * @return number
  */
 u32_t FIFO_UNUSED(fifo_t* fifo){
     return ((fifo->length + 1) - (fifo->input - fifo->output));
 }
 /**
  * @brief Used space in queue
  * @param fifo pointer
  * @return number
  */
 u32_t FIFO_USED(fifo_t* fifo){
     return (fifo->input - fifo->output);
 }
 /**
  * @brief get data or copy data
  * @param fifo pointer , data or buff , numbers , address , flag
  * @return 
  */
 static void FIFO_COPY(fifo_t* fifo, const void* src_or_dst, u32_t length, u32_t offset, u32_t FLAG){
     u32_t fifo_length = fifo->length + 1;
     u32_t fifo_element_size = fifo->element_size;
 
     offset &= fifo->length;
 
     length *= fifo_element_size;
     fifo_length *= fifo_element_size;
     offset *= fifo_element_size;
 
     u32_t tmp = MIN(length, fifo_length - offset);
     if(FLAG == _INPUT){
         memcpy((void *)fifo->address + offset, src_or_dst, tmp);
         memcpy((void *)fifo->address, src_or_dst + tmp, length - tmp);
     }else if(FLAG == _OUTPUT){
         memcpy((void *)src_or_dst, (const void *)fifo->address + offset, tmp);
         memcpy( (void *)src_or_dst + tmp, (const void *)fifo->address, length - tmp);
     }
 }
 /**
  * @brief input data to fifo
  * @param fifo pointer , data , len
  * @return  len
  */
 u32_t FIFO_INPUT(fifo_t* fifo, const void* data, u32_t length){
     u32_t balance_fifo_num = FIFO_UNUSED(fifo) ;
     if(!balance_fifo_num)
         return FAILURE;
     if(length > balance_fifo_num)
         length = balance_fifo_num;
     FIFO_COPY(fifo, data, length, fifo->input, _INPUT);
     fifo->input += length;
     return length;
 }
 /**
  * @brief output data to buff
  * @param fifo pointer , buf , len
  * @return 
  */
 u32_t FIFO_OUTPUT(fifo_t* fifo, void* buf , u32_t length){
     u32_t used_fifo_num = FIFO_USED(fifo);
     if(!used_fifo_num)
         return FAILURE;
     if(length > used_fifo_num)
         length = used_fifo_num;
     FIFO_COPY(fifo, buf, length, fifo->output, _OUTPUT);
     fifo->output += length;
     return length;
 }
 /**
  * @brief get queue head
  * @param fifo pointer
  * @return head address
  */
 void* FIFO_GET_HEAD(fifo_t* fifo){
     // fifo->address
     u32_t offset = 0;
     offset &= fifo->length;
     offset *= fifo->element_size;
     return (void*)fifo->address + offset;
 }

Main.c

 /****************************************************************
   *@file       : main.c
   *@description: 
   *@time       : 2023/08/21 20:27:14
   *@author     : Nick Xia
   *@blog       : https://aeneag.xyz
 *****************************************************************/
 #include "fifo.h"
 #include <stdio.h>
 
 //Defines a queue type
 typedef struct _fifo_struct_{
     u8_t data1;
     u8_t data2;
     u16_t data3;
     u32_t data4;
     u64_t data5;
 }__attribute__((packed)) data_t;
 
 //Declaring a circular queue
 u8_t g_fifo[fifo_total_size(64,sizeof(data_t))];
 
 /**
  * @brief main function
  * @param 
  * @return 0
  */
 int main(void){
     u32_t ret = FIFO_INIT((fifo_t*)g_fifo,64,sizeof(data_t));
     data_t data[3];
     int j = 1;
     for(int i= 0; i < 3; ++i){
         data[i].data1 = (u8_t)j;++j;
         data[i].data2 = (u8_t)j;++j;
         data[i].data3 = (u16_t)++j;
         data[i].data4 = (u32_t)j;++j;
         data[i].data5 = (u64_t)j;++j;
     }
     FIFO_INPUT((fifo_t*)g_fifo, (void*)data,3);
     FIFO_INPUT((fifo_t*)g_fifo, (void*)data,3);
     
 
     printf("used %d\n",FIFO_USED((fifo_t*)g_fifo));
     printf("unused %d\n",FIFO_UNUSED((fifo_t*)g_fifo));
 
     data_t get_buff;
     FIFO_OUTPUT((fifo_t*)g_fifo, (void*)&get_buff,1);
     printf("data1-%d, data2-%d, data3-%d, data4-%d, data5-%lld\n",get_buff.data1,get_buff.data2,get_buff.data3,get_buff.data4,get_buff.data5);
     printf("used %d\n",FIFO_USED((fifo_t*)g_fifo));
 
     data_t buff[4] = {0};
     FIFO_OUTPUT((fifo_t*)g_fifo, (void*)buff,4);
     for(int i = 0 ; i < 4 ; ++i){
     printf("data1-%d, data2-%d, data3-%d, data4-%d, data5-%lld\n",buff[i].data1,buff[i].data2,buff[i].data3,buff[i].data4,buff[i].data5);
     }
     printf("used %d\n",FIFO_USED((fifo_t*)g_fifo));
     printf("unused %d\n", FIFO_UNUSED((fifo_t*)g_fifo));
 
     FIFO_INPUT((fifo_t*)g_fifo, (void*)data,3);
     printf("used %d\n",FIFO_USED((fifo_t*)g_fifo));
     printf("unused %d\n", FIFO_UNUSED((fifo_t*)g_fifo));
     return 0;
 }

运行测试

003.png


    


公众号'艾恩凝'
个人公众号
个人微信
个人微信
    吾心信其可行,
          则移山填海之难,
                  终有成功之日!
                                  ——孙文
    评论
    0 评论
avatar

取消