当前位置: 首页> 文旅> 艺术 > 广东建筑信息平台_建站系统网站建设_百度软件中心下载_如何创建个人网站免费

广东建筑信息平台_建站系统网站建设_百度软件中心下载_如何创建个人网站免费

时间:2025/8/5 14:56:35来源:https://blog.csdn.net/weixin_52342399/article/details/144995776 浏览次数:0次
广东建筑信息平台_建站系统网站建设_百度软件中心下载_如何创建个人网站免费

        环形缓冲区(Circular Buffer),也称为循环队列或Ring Buffer,是一种非常实用的数据结构,尤其在生产者-消费者模型中,用于解决数据传输速度不匹配的问题。本文将深入讲解C语言中环形缓冲区的实现原理、关键概念以及使用方法,并结合图解进行形象的解释。

1. 什么是环形缓冲区?

        想象一个首尾相连的管道,数据从一端流入,从另一端流出。这就是环形缓冲区的基本模型。它使用一段固定大小的内存空间,通过读写指针的循环移动,实现高效的数据传递,避免了频繁的数据复制和移动。

2. 核心概念

        环形缓冲区就像一个首尾相连的圆环,数据在这个环上循环流动。我们使用数组来模拟这个环,并用两个指针 start(读指针)和 end(写指针)来跟踪数据的读写位置

  • 缓冲区大小(Length/Capacity): 缓冲区可以存储的最大数据量。
  • 读指针(Start/Head): 指向下一个要读取的数据的位置。
  • 写指针(End/Tail): 指向下一个要写入数据的位置。
  • 空闲位置: 为了区分缓冲区“满”和“空”的状态,通常会保留一个空闲位置。这意味着缓冲区实际可用的存储空间比其物理大小少 1。这是非常重要的设计,避免了 startend 指针重合时,无法区分满和空的状态。

3. 图解环形缓冲区

        假设我们有一个大小为 5 的数组来模拟环形缓冲区(实际有效存储空间为 4,需要保留一个空位)。

+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+0   1   2   3   4 (数组索引)^           ^|           |
start       end

初始状态(空): startend 指向同一个位置(通常是 0)。表示缓冲区没有任何数据。 

+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
^                   ^
|                   |
start             end首位相连,这其实end和start是重合的

写入数据: end 指针向后移动,并将数据写入到 end 指向的位置。

写入 'A'、'B'、'C':

+---+---+---+---+---+
| A | B | C |   |   |
+---+---+---+---+---+
^               ^
|               |
start           end

读取数据: start 指针向后移动,并读取 start 指向位置的数据。

读取 'A':

+---+---+---+---+---+
|   | B | C |   |   |
+---+---+---+---+---+^           ^|           |start         end

环绕:end 指针到达数组末尾时,它会“绕回”到数组的开头。

写入 'D' 和 'E':

+---+---+---+---+---+
| E | B | C | D |   |
+---+---+---+---+---+^       ^|       |start     end

缓冲区满:end 指针的下一个位置(环绕后)与 start 指针重合时,缓冲区就满了。此时不能再写入数据,除非先读取数据腾出空间。 

        如果继续写入,end 会追上 start,导致数据覆盖。为了避免这种情况,我们保留一个空位,即当 (end + 1) % length == start 时,就认为缓冲区已满。

 4. 环形缓冲区基本功能代码说明

 4.1 头文件函数、宏定义总览:

#ifndef _ring_buffer_h
#define _ring_buffer_htypedef struct {char *buffer; // int length; // 缓冲区空间大小int start; // 读指针位int end; // 写指针位
} RingBuffer;RingBuffer *RingBuffer_create(int length);void RingBuffer_destroy(RingBuffer *buffer);int RingBuffer_read(RingBuffer *buffer, char *target, int amount);int RingBuffer_write(RingBuffer *buffer, char *data, int length);void *RingBuffer_gets(RingBuffer *buffer);#define RingBuffer_available_data(B) (((B)->end - (B)->start + (B)->length) % (B)->length) #define RingBuffer_available_space(B) ((B)->length - RingBuffer_available_data(B) - 1)#define RingBuffer_full(B) (RingBuffer_available_data((B)) + 1 == (B)->length)#define RingBuffer_empty(B) (RingBuffer_available_data((B)) == 0)#define RingBuffer_puts(B, D) RingBuffer_write((B), bdata((D)), blength((D)))#define RingBuffer_get_all(B) RingBuffer_gets((B), RingBuffer_available_data((B)))#define RingBuffer_starts_at(B) ((B)->buffer + (B)->start)#define RingBuffer_ends_at(B) ((B)->buffer + (B)->end)#define RingBuffer_commit_read(B, A) ((B)->start = ((B)->start + (A)) % (B)->length) //将读指针向前移动A个位置。#define RingBuffer_commit_write(B, A) ((B)->end = ((B)->end + (A)) % (B)->length) // 将写指针向前移动A个位置。 #endif

 4.1.1 RingBuffer结构体

typedef struct {char *buffer; // 存储数据的缓冲区int length;   // 缓冲区总长度(包含一个空闲位置)int start;    // 读指针int end;      // 写指针
} RingBuffer;
  • buffer: 指向实际存储数据的内存区域。
  • length: 缓冲区的大小。为了区分空和满的状态,实际可用的存储空间是 length - 1
  • start: 读指针,指向下一个要读取的数据的位置。
  • end: 写指针,指向下一个要写入数据的位置。

4.1.2  宏定义:

这些宏定义简化了对环形缓冲区的操作和状态判断。

  • RingBuffer_available_data(B): 计算缓冲区中已有的数据量。使用模运算 % 实现环形效果。
  • RingBuffer_available_space(B): 计算缓冲区中可用的空间量。注意要减去 1,因为要保留一个空位。
  • RingBuffer_full(B): 判断缓冲区是否已满。
  • RingBuffer_empty(B): 判断缓冲区是否为空。
  • RingBuffer_starts_at(B): 获取 start 指针指向的地址。
  • RingBuffer_ends_at(B): 获取 end 指针指向的地址。
  • RingBuffer_commit_read(B, A): 将 start 指针向前移动 A 个位置。
  • RingBuffer_commit_write(B, A): 将 end 指针向前移动 A 个位置。

4.2 .c文件中的函数实现 

RingBuffer_create(int length) 创建环形缓冲区。

RingBuffer *RingBuffer_create(int length) {RingBuffer *buffer = calloc(1, sizeof(RingBuffer)); // 分配 RingBuffer 结构体内存check_mem(buffer); // 检查内存分配是否成功buffer->length = length + 1; // 缓冲区实际长度比length大1buffer->start = 0;buffer->end = 0;buffer->buffer = calloc(buffer->length, 1); // 分配缓冲区内存check_mem(buffer->buffer); // 检查内存分配是否成功return buffer;
error:if (buffer) { // 内存分配失败时释放已分配的内存,防止内存泄漏free(buffer);}return NULL;
}

RingBuffer_destroy(RingBuffer *buffer) 销毁环形缓冲区,释放内存。 

void RingBuffer_destroy(RingBuffer *buffer) {if (buffer) {free(buffer->buffer); // 释放缓冲区内存free(buffer);        // 释放 RingBuffer 结构体内存}
}

RingBuffer_write(RingBuffer *buffer, char *data, int length) 向缓冲区写入数据。 

int RingBuffer_write(RingBuffer *buffer, char *data, int length) {check(length > 0 && buffer != NULL && data != NULL, "Invalid inputs."); // 参数检查check(length <= RingBuffer_available_space(buffer), // 检查是否有足够的空间"Not enough space: %d request, %d available",RingBuffer_available_data(buffer), length);for (int i = 0; i < length; i++) {*(RingBuffer_ends_at(buffer)) = *(data + i); // 写入数据RingBuffer_commit_write(buffer, 1);         // 移动 end 指针}return length; // 返回实际写入的字节数
error:return -1; // 写入失败
}

RingBuffer_read(RingBuffer *buffer, char *target, int amount) 从缓冲区读取数据。 

int RingBuffer_read(RingBuffer *buffer, char *target, int amount) {check(amount > 0 && buffer != NULL && target != NULL, "Invalid inputs."); // 参数检查check_debug(amount <= RingBuffer_available_data(buffer), // 检查是否有足够的数据"Not enough in the buffer: has %d, needs %d",RingBuffer_available_data(buffer), amount);for (int i = 0; i < amount; i++) {*(target + i) = *(RingBuffer_starts_at(buffer)); // 读取数据RingBuffer_commit_read(buffer, 1);             // 移动 start 指针}return amount; // 返回实际读取的字节数
error:return -1; // 读取失败
}

RingBuffer_gets(RingBuffer *buffer) 获取缓冲区中的所有数据,但不修改缓冲区状态(即不移动 start 指针)。 

void *RingBuffer_gets(RingBuffer *buffer) {check(buffer != NULL, "Invalid inputs.");char *result = NULL;if (RingBuffer_empty(buffer)) { // 缓冲区为空result = calloc(1, 1);check_mem(result);*result = '\0';return result;} else { // 缓冲区非空int n = RingBuffer_available_data(buffer);result = calloc(1, n + 1); // 分配 n+1 个字节,用于存储字符串和空终止符check_mem(result);int i = 0;int current = buffer->start; // 保存 start 指针while (i < n) {result[i] = buffer->buffer[current];current = (current + 1) % buffer->length; // 循环移动 currenti++;}result[n] = '\0'; // 添加字符串结束符}return result;
error:if (result) free(result);return NULL;
}

 5. 环形缓冲区的优势及其应用领域

应用场景:

  • 生产者-消费者模型: 这是环形缓冲区最经典的应用场景。生产者向缓冲区写入数据,消费者从缓冲区读取数据,缓冲区起到了缓冲和解耦的作用。
  • 网络数据传输: 在网络编程中,环形缓冲区常用于接收和发送数据,解决网络速度和应用程序处理速度不匹配的问题。
  • 音频/视频处理: 音频和视频数据通常以流的形式传输,环形缓冲区可以有效地管理这些数据流。
  • 中断处理: 在嵌入式系统中,中断处理程序通常会将数据写入环形缓冲区,然后由主程序进行处理。

相比于其他数据结构的优势:

  • 高效的入队和出队操作: 只需要移动指针,不需要像普通队列那样移动大量数据。
  • 避免频繁的内存分配和释放: 使用固定大小的内存空间,提高了性能。
  • 适用于流式数据处理: 能够很好地处理连续不断的数据流。
关键字:广东建筑信息平台_建站系统网站建设_百度软件中心下载_如何创建个人网站免费

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: