C++环形缓冲区

2023-10-27

环形缓冲区(ring buffer)也称作循环缓冲区(cyclic buffer)、圆形队列(circular queue)、圆形缓冲区(circular buffer)。环形缓冲区并不是指物理意义上的一个首尾相连成“环”的缓冲区,而是逻辑意义上的一个环,因为内存空间是线性结构,所以实际上环形缓冲区仍是一段有长度的内存空间,是一个先进先出功能的缓冲区,具备实现通信进程对该缓冲区的互斥访问功能。

环形缓冲区的长度是固定的,在使用该缓冲区时,不需要将所有的数据清除,只需要调整指向该缓冲区的pHead、pValidWrite和pTail指针位置即可。pValidWrite指针最先指向pHead指针位置(环形缓冲区开头位置),数据从pValidWrite指针处开始存储,每存储一个数据,pValidWrite指针位置向后移动一个长度 ,随着数据的添加,pValidWrite指针随移动数据长度大小个位置。当pValidWrite指向pTail尾部指针,pValidWrite重新指向pHead指针位置(折行处理),并且覆盖原先位置数据内容直到数据存储完毕。

https://img-blog.csdnimg.cn/20210428110021121.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VtaW5nNQ==,size_16,color_FFFFFF,t_70#pic_center

一般构建一个环形缓冲区需要一段连续的内存空间以及4个指针:

pHead指针:指向内存空间中的首地址;

pTail指针:指向内存空间的尾地址;

pValidRead:指向内存空间存储数据的起始位置(读指针);

pValidWrite:指向内存空间存储数据的结尾位置(写指针)。

当申请完内存以及指针定义完毕后,环形缓冲区说明及使用如下:

1.该段内存空间的长度是Len = pTail-pHead;

2.pValidRead是读数据的起始位置,当读取完N数据之后要移动N个单位长度的偏移,当有addlen长度的数据要存入到环形缓冲区,若addlen + pValidWrite > pTail时,pValidWrite将存入len1 = pTail - pValidWrite个数据长度,然后pValidWrite回到pHead位置,将剩下的len2 = addlen - len1个数据从pHead开始存储并覆盖到原来的数据内容。

3.pValidWrite是写数据的起始位置,当存入N个数据之后要移动N个单位长度的偏移,pValidRead是读数据的起始位置,当读取N个数据之后要移动N个单位长度的偏移。当要addlen长度的数据要从环形缓冲区读取,若addlen + pValidRead > pTail时,pValidRead 将读取len1 = pTail - pValidRead 个数据长度,然后pValidRead 回到pHead位置,将剩下的len2 = addlen - len1个数据从pHead开始读取完毕。

文件1:ringbuffer.h

Ringbuffer.h
#pragma once
#include<stdio.h>
#include<stdlib.h>   //引入malloc动态分配内存
#include<stdint.h>  //引入uint32_t,32位无符号整形
#include<string.h>  //引入memcpy内存拷贝函数

void InitRingBuffer();   
void FreeRingBuffer();
int WriteRingBuffer(uint32_t *pData,int Data_Size);
int ReadRingBuffer(uint32_t* pData, int Data_Size);
int GetRingBufferValidSize();

 文件2:ringbuffer.cpp

Ringbuffer.cpp
#include"RingBuffer.h"
#define BUFFER_SIZE 16  //环形存储区长度

uint32_t* pHead = NULL; //环形缓冲区首地址
uint32_t* pTail = NULL; //环形缓冲区尾地址
uint32_t* pRead = NULL; //读指针
uint32_t* pWrite = NULL; //写指针
int BUFFER_VALID_SIZE; //环形存储区存储的数据大小

//初始化环形缓冲区
void InitRingBuffer()
{
	if (pHead == NULL)
	{
		pHead = (uint32_t*)malloc(BUFFER_SIZE);
	}
	pRead = pHead;
	pWrite = pHead;
	pTail = pHead + BUFFER_SIZE;
	BUFFER_VALID_SIZE = 0;
}
//释放环形缓冲区
void FreeRingBuffer()
{
	if (pHead != NULL) //当存在缓冲区时
	{
		free(pHead);  //释放
	}
	pHead = NULL;  //重新令环形器起始地址为空
	BUFFER_VALID_SIZE = 0;
}
//写命令
int WriteRingBuffer(uint32_t* pData, int Data_Size)
{
	if (pHead == NULL)  //缓冲区未初始化
	{
		printf("Writing Failed! The ringbuffer is not inited!\n");
		return -1;
	}
	if (Data_Size > BUFFER_SIZE)  //加入数据过多
	{
		printf("Writing Failed! The input data size exceeds the ringbuffer size!\n");
		return -2;
	}
	if (pWrite+Data_Size>pTail)
		//将数据存入缓冲区,但是数据需要分成两段
	{
		int Data_Part1 = pTail - pWrite; //存放在靠近尾部的Data部分
		int Data_Part2 = Data_Size - Data_Part1;//存放在靠近头部的Data部分
		memcpy(pWrite, pData, Data_Part1 * sizeof(uint32_t));
		memcpy(pHead, pData+Data_Part1, Data_Part2 * sizeof(uint32_t));
		pWrite = pHead + Data_Part2; //更新写指针
		BUFFER_VALID_SIZE = BUFFER_SIZE;
	}
	else //不用分段
	{
		memcpy(pWrite, pData, Data_Size * sizeof(uint32_t));
		pWrite += Data_Size;//更新写指针
		BUFFER_VALID_SIZE += Data_Size;
	}
	return 0;
}
//读命令
int ReadRingBuffer(uint32_t* pData, int Data_Size)
{
	if (pHead == NULL)  //缓冲区未初始化
	{
		printf("Reading Failed! The ringbuffer is not inited!\n");
		return -1;
	}

	if (Data_Size > pTail-pHead)  //读出数据过多,超出BUFFER大小
	{
		printf("Reading Failed! The reading data size exceeds the ringbuffer size!\n");
		return -2;
	}
	//无数据读出
	if (Data_Size == 0)
	{
		printf("Reading Failed! No data exists!\n");
		return 0;
	}
	//判断是否需要分成两段读出
	if (pRead + Data_Size > pTail)
	{
		int Data_Part1 = pTail - pRead; 
		int Data_Part2 = Data_Size - Data_Part1;
		memcpy(pData, pRead, Data_Part1 * sizeof(uint32_t));
		memcpy(pData + Data_Part1, pHead, Data_Part2 * sizeof(uint32_t));
		pRead = pHead + Data_Part2;
		BUFFER_VALID_SIZE -= Data_Size;
	}
	else
	{
		memcpy(pData, pRead, Data_Size * sizeof(uint32_t));
		pRead += Data_Size;
		BUFFER_VALID_SIZE -= Data_Size;
	}
	return 1;
}
//获取当前缓冲区有效长度,即存储的数据个数
int GetRingBufferValidSize()
{
	return BUFFER_VALID_SIZE;
}

 文件3:main.cpp

#include"RingBuffer.h"
#include<iostream>

int main()
{
	//InitRingBuffer();
	std::cout << "---------------------------------" << std::endl;
	std::cout << "Input your choice:" << std::endl;
	std::cout << "1. Initializatize the ring buffer." << std::endl;
	std::cout << "2. Delete the ring buffer." << std::endl;
	std::cout << "3. Write the ring buffer." << std::endl;
	std::cout << "4. Read the ring buffer." << std::endl;
	std::cout << "---------------------------------" << std::endl;
	int choice;
	
	int Len;   //存储当前环形缓冲区的数据长度
	uint32_t ReadData[5];  //存储读出的数据
	uint32_t data[5] = {1,2,3,4,5,};   //假设有5个读入的数据
	int Data_Size = 5;  //数据大小,占20个字节

	while (1)
	{
		std::cin >> choice;
		switch (choice)
		{
		case 1: InitRingBuffer();
			std::cout << "缓冲区初始化成功!" << std::endl;
			break; 
		case 2: FreeRingBuffer();
			std::cout << "缓冲区释放成功!" << std::endl;
			return 1;
		case 3:
			std::cout << "执行数据拷贝!" << std::endl;
			WriteRingBuffer(data,  Data_Size);
		    break;
		case 4:
			Len = GetRingBufferValidSize();
				std::cout << "当前缓冲区存储的数据个数:" << Len << std::endl;
				ReadRingBuffer(ReadData, Data_Size);
				if (Len > 0)
				{
					std::cout << "读取环形存储区的数据为:" << std::endl;
					for (int i = 0; i < Len; i++)
					{
						std::cout << ReadData[i] << std::endl;
					}
				}
				else
				{
					std::cout << "当前缓冲区无数据!" << std::endl;
				}
		    break;
		default:
			break;
		}
	}
	system("pause");
	return 1;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++环形缓冲区 的相关文章

  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 属性对象什么时候创建?

    由于属性实际上只是附加到程序集的元数据 这是否意味着属性对象仅根据请求创建 例如当您调用 GetCustomAttributes 时 或者它们是在创建对象时创建的 或者 前两个的组合 在由于 CLR 的属性扫描而创建对象时创建 从 CLR
  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • 为什么禁止在 constexpr 函数中使用 goto?

    C 14 对你能做什么和不能做什么有规则constexpr功能 其中一些 没有asm 没有静态变量 看起来相当合理 但标准也不允许goto in constexpr功能 即使它允许其他控制流机制 这种区别背后的原因是什么 我以为我们已经过去
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • GDK3/GTK3窗口更新的精确定时

    我有一个使用 GTK 用 C 语言编写的应用程序 尽管该语言对于这个问题可能并不重要 这个应用程序有全屏gtk window与单个gtk drawing area 对于绘图区域 我已经通过注册了一个刻度回调gtk widget add ti
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐

  • 超详细的ARM架构安装Mysql8.0

    一 安装环境 系统 Mac Linux版本 CentOS 9 二 安装 1 先卸载MariaDB 在CentOS中默认安装有MariaDB 是MySQL的一个分支 主要由开源社区维护 CentOS 7及以上版本已经不再使用MySQL数据库
  • 迁移学习&finetune详解

    文章目录 一 为什么要用迁移学习 二 几种方式 三 三种方式的对比 四 具体训练策略建议 一 为什么要用迁移学习 1 站在巨人的肩膀上 在已经上线的基础模型的效果可以接受的情况下 表明模型有效 可以分辨数据集基础特征 没有必要重复造轮子 每
  • MySQL中删除id为最小的数据

    方法1 delete from 表名 where id in select id from select min id id from 表名 c1 t1 方法2 delete from 表名 order by id asc limit 1
  • 链表随机指针

    我卡住的一个原因是 我曾想便利两次 想在第二遍的同时把两个链表分离开 结果失败了 必须三遍 以为random指针很可能指到前面已经断开的节点 而这个还是不用哈希的方式 因此如果前面的链表断开了 就都乱套了 Definition for a
  • Android Studio模拟器启动后不停闪烁(已玄学解决)

    问题描述 Android Studio模拟器启动后不停闪烁 解决方法 右侧点击Device Manager打开设备管理 点击修改标志 将Graphics 图样 换成Software 软件 点击Finish 这个方法是网上找的 但是不好使TA
  • Linux命令 - cp命令

    Linux命令 cp命令 cp 是copy的缩写 Linux中 cp命令用来复制文件或者目录 一般情况下 shell会设置一个别名 在命令行下复制文件时 如果目标文件已经存在 就会询问是否覆盖 不管你是否使用 i参数 但是如果是在shell
  • python图像差分法目标检测_运动目标检测(4)—背景差分法

    背景减法利用图像序列中的当前帧和事先确定的背景参考模型间的差异比较 来确定运动物体位置 是一种基于统计学原理的运动目标检测的方法 这种方法的性能取决于背景建模技术 Gloyer等人使用单高斯模型的思路 但常常不能准确地描述背景模型 1999
  • Android调用系统发送短信界面

    很多软件都有分享的功能 不少是支持短信分享的 其实就是调用系统发送短信的Activity 代码实现非常简单 发送短信 param smsBody private void sendSMS String smsBody Uri smsToUr
  • Elasticsearch基本概念及CRUD常用操作

    Elasticsearch基本概念及CRUD常用操作 一 介绍 二 REST 风格 三 索引CRUD 3 1 创建索引 3 2 查询索引 3 3 删除索引 四 文档CRUD 4 1 添加文档 4 1 1 普通添加 随机ID 4 1 2 普通
  • Mybatis学习笔记-Mabatis缓存

    Mybatis学习笔记 Mybatis缓存 缓存就是内存中的数据 常常来自对数据库查询结果的保存 使用缓存 我们可以避免频繁的与数据库进行交互 进而提高响应速度 MyBatis提供了对缓存的支持 分为一级缓存和二级缓存 可以通过一下图解来理
  • 解决github访问不了的方法

    1 打开网站http tool chinaz com dns 在A类型的查询中输入 github com 找到最快访问的ip地址 并复制下来 2 修改系统文件的hosts文件 进入C Windows System32 drivers etc
  • 董事聘任书

    EX 10 38 4 future s1a3 ex1038 htm DIRECTOR OFFER LETTER Exhibit 10 38 The Future Education Group Inc Room 501 Gaohelanfe
  • 前端实习面试题(自己当笔记用)

    一 CSS 01 Flex布局 display flex 在父元素设置 子元素受弹性盒影响 默认排成一行 如果超出一行 按比例压缩 容器属性 1 flex direction属性 取值 row 默认 row reverse column c
  • pycharm导入未安装的第三方库,无错误提示,急需

    pycharm导入未安装的第三方库 无错误提示 急需 如下图所示 第三方库pandas未安装 但是pycharm没有错误 如下图所示 第三方库pandas未安装 但是pycharm没有错误 pandas 并未i安装 pandas下面不是应该
  • Python基础第二话:列表+元组+字典+集合,学基础还得是这....

    Python基础知识第二话 Python基础知识第一话 这一章的知识紧接上一章 零基础的小伙伴可以从上 一章学起来 当然 你也可以收藏起来慢慢学习 学习是不可操之过急的啦 列表 Python学习交流Q群 906715085 print 创建
  • Gradle 详解

    原文地址 深入理解Android 一 Gradle详解 InfoQ 备用地址 Gradle史上最详细解析 AndroidM 博客园 以下内容为复制 粘贴 建议看原文 编者按 随着移动设备硬件能力的提升 Android 系统开放的特质开始显现
  • vue三种方式实现父子间通信

    当前有子组件Student和父组件App 如何让Student发送自己的学生名 App接收并显示 有三种方法 父传子 props
  • mysql 存储过程如何声明多个变量赋值_mysql存储过程:定义变量,赋值,判断,输出...

    1 定义变量 赋值 判断 输出 DROP PROCEDURE IF EXISTS proc first 如果proc first存在就删除 create procedure proc first 创建存储过程 begin 定义变量 DECL
  • Sql_Server中如何判断表中某列是否存在

    判断表AA中是否存在AA ID这一列 如果不存在 则新增 IF NOT EXISTS SELECT 1 FROM syscolumns INNER JOIN sysobjects ON sysobjects id syscolumns id
  • C++环形缓冲区

    环形缓冲区 ring buffer 也称作循环缓冲区 cyclic buffer 圆形队列 circular queue 圆形缓冲区 circular buffer 环形缓冲区并不是指物理意义上的一个首尾相连成 环 的缓冲区 而是逻辑意义上