C语言构建环形缓冲区

本文详细介绍了环形队列的概念、结构及其在数据收发、程序间数据交换等场景中的应用。文中提供了两种实现方式:一种是预留1个空位,另一种是通过附加满空标志位。每种实现包括数据结构、操作规则、函数接口以及测试案例。此外,还讨论了getchar()与输入缓冲区的关系。
摘要由CSDN通过智能技术生成

它逻辑上是一个首尾相连的FIFO结构,具体实现上采用简单的线性数组。通过额外的辅助标志(head、tail)能很快知道队列的使用情况(是满还是为空)。正因为其简单高效的原因,甚至在硬件都实现了环形队列。

环形队列广泛用于网络数据收发、程序间的大量数据交换(比如内核与应用程)、硬件接收大量数据

1、环形缓冲区原理

image-20210828164243830

  • 环列队列逻辑上将数组元素array[0]与array[LEN-1]连接起来,形成一个存放队列的环形空间。实际操作中为了方便读写,采用headtail分别指向可以读的位置和可以写的位置。
  • 环形队列的关键是判断队列为空,还是为满。一般有两种方法:
    • 一是附加一个标志位tag
    • 当head赶上tail,队列空,则令tag=0
    • 当tail赶上head,队列满,则令tag=1
    • 二是在队尾结点与队首结点之间留有1个元素的空间
      • 队列空: head==tail
      • 队列满: (tail+1)% MAXN ==head

2、预留1个空位的环形队列

  • 开始时head和tail指向同一个地址,但随着数据的push和poll,head和tail之间永远预留一个单元空间。如下图所示即为一个满队列。但箭头所指的位置不准确。tail应该指向空位,head指向9。即从头出去,从尾巴进来。

image-20210828164743425

  • 数据结构
struct ring_queue{  
    unsigned int head;   
    unsigned int tail;  
    unsigned int size; 	//环形缓冲区容量
    int *array;   		//实际缓冲区(数组)的首地址
}; 
  • 规则
    • head指向可读位置(地址存有数据),tail指向可写位置(地址无数据)。
    • 初始化状态: head = tail = 0;
    • 判定队列为空:head == tail
    • 队列为满:**( (tail+1) % SIZE ) == head **
    • 入队操作:若队列不满,则写入。
    • 出队操作:若队列不空,则读出。
    • 缓冲区必须是连续的内存空间,可以通过静态数组变量或局部数组变量的方式定义,但绝不能从堆上分配(malloc),因为malloc分配的内存空间是不连续的!!!
  • 头文件
#ifndef __RINGQ_H__
#define __RINGQ_H__

#ifdef __cplusplus
extern "C" {
#endif

    
typedef struct {  
    unsigned int head;   
    unsigned int tail;
    unsigned int size;
    int *array;   
}RINGQ;    

#define ringq_is_empty(q) (q->head == q->tail)
#define ringq_is_full(q) (((q->tail+1)%q->size) == q->head )

int ringq_init(RINGQ * ringqp, int * array_ptr, unsigned size);
int ringq_free(RINGQ * ringqp);
int ringq_push(RINGQ * ringqp,int data);
int ringq_poll(RINGQ * ringqp,int * val);
void ringq_display(RINGQ * ringqp);

#ifdef __cplusplus
}
#endif

#endif
  • c文件
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include "ringq.h"
  4 
  5 int ringq_init(RINGQ * ringqp, int * array_ptr, unsigned size)
  6 {
  7     /*
  8        不能从堆里分配空间!!!
  9        因为堆里的空间不是连续的,而是通过链表链接的一个空间串
 10     if(!(ringqp->array = (int *)malloc(size))){
 11         printf("Malloc failed!\n");
 12        return -1;
 13     }
 14     */
 15     ringqp->array = array_ptr;
 16     ringqp->size = size;
 17     ringqp->head = 0;
 18     ringqp->tail = 0;
 19     return 0;
 20 }
 21 
 22 int ringq_free(RINGQ * ringqp)
 23 {
 24     free(ringqp->array);
 25     return 0;
 26 }
 27 
 28 
 29 int ringq_push(RINGQ * ringqp,int data)
 30 {
 31     if(ringq_is_full(ringqp))
 32     {
 33         printf("ringq is full.\n");
 34         return -2;
 35     }
 36     ringqp->array[ringqp->tail] = data;
 37     ringqp->tail = (ringqp->tail + 1) % ringqp->size ;
 38     return 0;
 39 }
 40 
 41 
 42 int ringq_poll(RINGQ * ringqp,int * val)
 43 {
 44    if(ringq_is_empty(ringqp))
 45     {
 46         printf("ringq is empty.\n");
 47         return -3;
 48     }
 49     *val = ringqp->array[ringqp->head];
 50     ringqp->head = (ringqp->head + 1) % ringqp->size ;
 51     return 0;
 52 }
 53 
 54 void ringq_display(RINGQ * ringqp)
 55 {
 56     int  i =0;
 57     unsigned head = ringqp->head;
 58     unsigned tail = ringqp->tail;
 59     unsigned size = ringqp->size;
 60 
 61     if(ringq_is_empty(ringqp))
 62     {
 63         printf("ringq is empty.\n");
 64         return;
 65     }
 66     while(head != tail){
 67         printf("%04d ",ringqp->array[head]);
 68         i++;
 69         if(i == 5){
 70             printf("\n");
 71             i = 0;
 72         }
 73         head = (head + 1)%(size);
 74     }
 75     printf("\n");
 76     return;
 77 }
  • 测试文件
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include "ringq.h"
  4 
  5 #define RQ_SIZE 50 
  6 
  7 int main(void)
  8 {
  9     int data_in = 0;
 10     int data_out = 0;
 11     int select = 0;
 12 
 13     int array[RQ_SIZE]={};
 14     RINGQ rq, *rqp;
 15     rqp = &rq;
 16 
 17     ringq_init(rqp, array, RQ_SIZE);
 18     ringq_display(rqp);
 19 
 20     int index = RQ_SIZE - 1;    //allways a bank between head and tail
 21     while (index > 0){
 22         ringq_push(rqp,1);
 23         index -= 1;
 24     }
 25 
 26     ringq_display(rqp);
 27 
 28     while (index < RQ_SIZE-1){
 29         ringq_poll(rqp,&data_out);
 30         index += 1;
 31     }
 32 
 33     ringq_display(rqp);
 34 
 35     while (index > 0){
 36         ringq_push(rqp,2);
 37         index -= 1;
 38     }
 39 
 40     ringq_display(rqp);
 41     
 42    return 0;
 43 }

  • 实验结果

image-20210828223651731

  • 测试文件2
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include "ringq.h"
  4 
  5 #define RQ_SIZE 5 
  6 
  7 int main(void)
  8 {
  9     int data_in = 0;
 10     int data_out = 0;
 11     int select = 0;
 12 
 13     int array[RQ_SIZE]={};
 14     RINGQ rq, *rqp;
 15     rqp = &rq;
 16 
 17     ringq_init(rqp, array, RQ_SIZE);
 18     ringq_display(rqp);
 19       
 42 
 43 loop:  puts("push or poll or quit? [i/o/q]");
 44     select = getchar();
 45     getchar();  //丢弃回车
 46     switch(select){
 47         case 'i':
 48             printf("The push data is:");
 49             scanf("%d",&data_in);
 50             getchar();  //丢弃回车
 51             ringq_push(rqp, data_in);
 52             break;
 53         case 'o':
 54             ringq_poll(rqp, &data_out);
 55             printf("%d poll successfull.\n",data_out);
 56             break;
 57         case 'q':
 58             
 59             return 0;
 60         default:
 61             printf("Wrong choice!enter i or o!\n");
 62     }
 63     ringq_display(rqp);
 64     printf("\n");
 65     goto loop;
 66 }
  • 实验结果

image-20210828224608118

image-20210828224656414

3、附加满空标志位的环形队列

  • 数据结构
typedef struct ringq{
   int head;
   int tail;
   int tag ;
   int size ;
   int space[RINGQ_MAX];

}RINGQ;
  • 规则
    • 初始化状态: q->head = q->tail = q->tag = 0;
    • 队列为空:(q->head == q->tail) && (q->tag == 0)
    • 队列为满**: ((q->head == q->tail) && (q->tag == 1))**
    • 入队操作:如队列不满,则写入q->tail = (q->tail + 1) % q->size ;
    • 出队操作:如果队列不空,则从head处读出。下一个可读的位置在 q->head = (q->head + 1) % q->size
  • 头文件
#ifndef __RINGQ_H__
#define __RINGQ_H__

#ifdef __cplusplus
extern "C" {
#endif

#define QUEUE_MAX 20
typedef struct ringq{
   int head;
   int tail;
   int tag ;
    int size ;
   int space[QUEUE_MAX];
}RINGQ;


extern int ringq_init(RINGQ * p_queue);
extern int ringq_free(RINGQ * p_queue);
extern int ringq_push(RINGQ * p_queue,int data);
extern int ringq_poll(RINGQ * p_queue,int *p_data);
#define ringq_is_empty(q) ( (q->head == q->tail) && (q->tag == 0))
#define ringq_is_full(q) ( (q->head == q->tail) && (q->tag == 1))
#define print_ringq(q) printf("ring head %d,tail %d,tag %d\n", q->head,q->tail,q->tag);
#ifdef __cplusplus
}
#endif

#endif
  • 实现代码
#include <stdio.h>
#include "ringq.h"

int ringq_init(RINGQ * p_queue)
{
   p_queue->size = QUEUE_MAX ;
   p_queue->head = 0;
   p_queue->tail = 0;
   p_queue->tag = 0;
   return 0;
}

int ringq_free(RINGQ * p_queue)
{
    return 0;
}
//Write data to the queue
int ringq_push(RINGQ * p_queue,int data)
{
    print_ringq(p_queue);
    if(ringq_is_full(p_queue))
    {
        printf("ringq is full\n");
        return -1;
    }
    p_queue->space[p_queue->tail] = data;
    p_queue->tail = (p_queue->tail + 1) % p_queue->size ;
    if(p_queue->tail == p_queue->head)
    {
        p_queue->tag = 1;
    }
    return p_queue->tag ;
}
//Get data from the queue
int ringq_poll(RINGQ * p_queue,int * p_data)
{
    print_ringq(p_queue);
    if(ringq_is_empty(p_queue))
    {
        printf("ringq is empty\n");
        return -1;
    }
    *p_data = p_queue->space[p_queue->head];
    p_queue->head = (p_queue->head + 1) % p_queue->size ;
    if(p_queue->tail == p_queue->head)
    {
        p_queue->tag = 0;
    }
    return p_queue->tag ;
}
  • 测试代码
//请参考上节的test.c

参考资料【转】环形队列理论(C语言) - 程序天空下的骆驼 - 博客园 (cnblogs.com)

环形缓冲区C语言实现可以参考以下步骤: 首先,需要创建一个包含所需函数和变量声明的头文件。可以使用引用中提供的RingBuffer.h头文件作为起点。该头文件中定义了环形缓冲区所需的类型和函数声明。 接下来,在源代码文件中引用该头文件,并实现其中声明的函数。我们可以使用引用中提供的main.c代码作为参考。在代码中,可以看到头文件RingBuffer.h被引用,并且在代码中实现了InitRingBuffer、writeRingbuffer、readRingbuffer、releaseRingbuffer等函数。 在InitRingBuffer函数中,首先对环形缓冲区进行初始化。这个函数接受一个指向缓冲区的指针和缓冲区的大小作为参数,并将头指针和尾指针初始化为0。 在writeRingbuffer函数中,首先检查缓冲区是否已满。如果缓冲区已满,则不进行写操作,否则将数据写入缓冲区,并将头指针向前移动一个位置。 在readRingbuffer函数中,首先检查缓冲区是否为空。如果缓冲区为空,则不进行读操作,否则将数据从缓冲区读取出来,并将尾指针向前移动一个位置。 getRingbufferValidLen函数用于获取当前缓冲区中有效数据的长度,即头指针与尾指针之间的距离。 最后,在使用完环形缓冲区后,需要调用releaseRingbuffer函数来释放资源。 综上所述,环形缓冲区C语言实现包括引用中的RingBuffer.h头文件和引用中的main.c源代码文件,其中实现了InitRingBuffer、writeRingbuffer、readRingbuffer、getRingbufferValidLen和releaseRingbuffer等函数。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leon_George

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值