使用vector<char>作为输入缓冲区

news2024/11/15 5:09:50

一、引言

  当我们编写代码:实现网络接收、读取文件内容等功能时,我们往往要在内存中开辟一个输入缓冲区(又名:input buffer/读缓冲区)来存贮接收到的数据。在C++里面我们可以用如下方法开辟输入缓冲区。

①使用C语言中的数组:

char buf[100] = {0};

②使用malloc/new动态分配内存:

char *pBuf = new char[100];

③使用std::string

string sBuf;

④使用vector<char> / vector<unsigned char>

vector<char> vecBuf(100);

在这里面推荐使用方法④作为输入缓冲区。方法①在栈中开辟空间,对于大数组可能会有栈内存不够的问题。方法②在堆上分配内存,但是使用完需要程序员自行手动释放(delete pBuf),而且需要一个额外的变量记录申请空间的大小。方法③只能处理字符串,不能处理二进制数据。下面具体阐述使用vector<char>作为输入缓冲区的优势。

二、使用vector<char>作为输入缓冲区的优势

(一)跟方法①相比,vector<char>可以在程序运行时调整大小

例子1:

main.cpp

#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>

// 通过stat结构体 获得文件大小,单位字节
size_t getFileSize1(const char* fileName) {

	if (fileName == NULL) {
		return 0;
	}
	// 这是一个存储文件(夹)信息的结构体,其中有文件大小和创建时间、访问时间、修改时间等
	struct stat statbuf;

	// 提供文件名字符串,获得文件属性结构体
	stat(fileName, &statbuf);

	// 获取文件大小
	size_t filesize = statbuf.st_size;
	return filesize;
}


int main()
{
	const char *fileName = "test.txt";
	std::ifstream ifs(fileName);
	int nFileSize = getFileSize1(fileName);
	char buf[100] = { 0 };
	ifs.read(buf, sizeof(buf));
	printf("%s", buf);
	return 0;
}

test.txt

hello world!

运行效果:

上述的例子中,定义一个大小为100字节的数组buf,一次性读取文件test.txt中的内存,并保存到buf里面,然后打印。该代码存在的问题是:假如文件test.txt中的内容非常多,超过数组的最大容量(100个字节),则超出数组容量外(超过100个字节之外)的数据会丢失。针对该问题我们可以尝试将上述代码优化为例子2。

例子2:

我们将例子1中的语句 char buf[100] = { 0 };  修改为:char buf[nFileSize] = { 0 };

结果编译报错了: 

在例子2中,我们尝试将数组buf的大小定义为要读取的文件的大小。很明显,这样是不行的,因为定义数组的时候,数组的大小必须确定,并且得是整型。我们继续优化代码。

例子3:

main.cpp

#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>

// 通过stat结构体 获得文件大小,单位字节
size_t getFileSize1(const char* fileName) {

	if (fileName == NULL) {
		return 0;
	}
	// 这是一个存储文件(夹)信息的结构体,其中有文件大小和创建时间、访问时间、修改时间等
	struct stat statbuf;

	// 提供文件名字符串,获得文件属性结构体
	stat(fileName, &statbuf);

	// 获取文件大小
	size_t filesize = statbuf.st_size;
	return filesize;
}


int main()
{
	const char *fileName = "test.txt";
	std::ifstream ifs(fileName);
	int nFileSize = getFileSize1(fileName);
	std::vector<char> vecBuf(nFileSize);
	ifs.read(&vecBuf[0], vecBuf.size());
	for (const auto& e : vecBuf)
	{
		std::cout << e;
	}
	return 0;
}

运行效果如下:

例子3使用了vector<char>,所以可以在程序运行过程中调整大小(可以用resize()调整vector大小)。从而解决例子2中的问题。可能有些朋友会说用方法②“使用malloc/new动态分配内存”,不一样可以吗?确实是可以。但是vector<char>相当于对malloc/new进行了一层封装,使用起来更方便。而且不用手动调用delete函数释放内存,避免内存泄漏。

(二)跟方法②相比,vector<char>提供了各种方法

使用vector::reserve预分配内存
使用vector::size的记录缓冲区位置
使用vector::resize增长/清除缓冲区
使用&your_vector[0]转换为C缓冲区
使用vector::swap转换缓冲区所有权

例子4:

int bufsize = 4096;
char *pBuf = new char[bufsize];
int recv = read(sock, pbuf, bufsize)

例子4是一个网络接收的小demo。可以看到使用new的方式,需要额外增加一个变量bufsize来存贮缓冲区的大小。我们可以用vector<char>优化如下:

例子5:

std::vector<char> buf(4096); // create buffer with preallocated size
int recv = read( sock, &buf[0], buf.size() );

可以看到vector已经提供了size()方法来记录缓冲区的大小,不需要再额外增加变量了。所以使用vector<char>更方便,而且离开作用域自动释放内存,不需要手动delete,更安全。

(三)跟方法③相比,vector<char>可以存贮二进制数据

例子6:

main.cpp

#include <iostream>
#include <vector>
#include <string>

using namespace std;


int main()
{
	string strBuf = "abc\0ef";
	cout << strBuf << endl;
	
	std::vector<char> vecBuf = { 'a', 'b', 'c', '\0', 'e', 'f'};
	for (const auto& e : vecBuf)
	{
		std::cout << e;
	}
	return 0;
}

运行效果如下:

 可以看到使用std::string丢失了'\0'之后的数据,但是vector<char>不会。所以std::string只能存贮字符串,不能存贮二进制数据。二进制数据中可能会包含0x00(即:'\0'),刚好是字符串结束标志,使用std::string会有截断问题。所以对于二进制数据的保存(比如保存图片,网络接收)我们得要用vector<char>,不要用string。


三、总结

综上所述。我们首选vector<char>作为输入缓冲区。

参考:

What is the advantage of using vector<char> as input buffer over char array?

How do I use vector as input buffer for socket in C++

A more elegant way to use recv() and vector<unsigned char>

What are differences between std::string and std::vector<char>?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356771.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring教程】1.Spring概述

1、概述 1.1、Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单性、可测…

Android Compose Bloom 项目实战 (四) : 主页

1. 前言 上篇文章 我们实现了 Compose Bloom项目的开发页&#xff0c;这篇文章接着上文&#xff0c;来介绍主页的开发。 2. 分析页面布局 根据UI稿我们可知&#xff0c;这个页面有一个底部切换的BottomBar&#xff0c;还有一个搜索框&#xff0c;一个横向的列表以及一个竖向的…

RNN GRU模型 LSTM模型图解笔记

RNN模型图解引用RNN模型GRULSTM深度RNN双向循环神经网络引用 动手学深度学习v2–李沐 LSTM长短期记忆网络3D模型–B站up梗直哥丶 RNN模型 加入了一个隐变量&#xff08;状态)&#xff0c;隐变量由上个隐变量和上一个输入而更新&#xff0c;这样模型就可以达到具有短期记忆的效…

加油站会员管理小程序实战开发教程10

上一篇我们介绍了计算距离及到店导航的功能,本篇我们介绍一下今日油价的功能。 如果要按日显示最新的数据,那么我们首先需要有数据源来存放每日的油价数据。这里涉及数据源的时候要考虑你的数据是只录入一条,还是每日录入一条。 录入一条呢,比较简单,但有个问题是如果我…

制作for arm64 cpu架构的docker镜像

我前段时间买了个阿里云&#xff0c;没有留意CPU的架构是ARM的&#xff0c;结果部署系统的时候就发现出问题了&#xff0c;部署在docker里的容器实例根本跑不起来&#xff0c;提示什么执行文件格式错误&#xff08;“exec format error”&#xff09;。 究其原因&#xff0c;…

spring注解方式整合Dubbo源码解析

系列文章目录 前言 本节我们的Dubbo源码版本基于2.6.x 在前一章我们的整合案例中&#xff0c;我们有几个比较关键的步骤&#xff1a; 在启动类上标注了EnableDubbo注解在provider类上面标注了Service注解来提供dubbo服务在消费的时候通过Reference注解引入dubbo服务在配置文件…

软考中级-面向对象

面向对象基础&#xff08;1&#xff09;类类分为三种&#xff1a;实体类&#xff08;世间万物&#xff09;、接口类&#xff08;又称边界类&#xff0c;提供用户与系统交互的方式&#xff09;、控制类&#xff08;前两类之间的媒介&#xff09;。对象&#xff1a;由对象名数据&…

【jumpServer 功能梳理】

用户管理 1.1 用户列表 创建jumpServe 账号 ;角色分为用户 管理员&#xff1b;更新账号信息&#xff1b;查看用户详情以及授权的资产&#xff1b; 1.2 用户组 用户组&#xff0c;这个组的意义在于用一个统称对接资源&#xff1b;用户组包含多个用户&#xff0c;可以操作增加删除…

LeetCode初级算法题:两数之和+斐波拉契数列多种java解法

目录7 两数之和题目描述&#xff1a;解题思路与代码暴力解法&#xff1a;解法一&#xff1a;二分查找解法二&#xff1a;双指针2 斐波那契数列题目描述&#xff1a;解题思路与代码![请添加图片描述](https://img-blog.csdnimg.cn/d06a95d7989b4794bd7f5f02fbd6f87e.png)解法一&…

spring之使用Spring的AOP

文章目录前言一、准备工作1、添加相应的依赖2、添加相应的命名空间3、创建目标类4、创建切面二、使用AOP1.在切面类中编写增强代码以及切点表达式2、开启aspectj的自动代理3、测试类4、测试结果前言 Spring对AOP的实现包括以下三种方式 1、Spring框架结合AspectJ框架实现的AOP…

自抗扰控制ADRC之反馈控制律(NLSEF)

目录 前言 1.非线性状态误差反馈控制律(NLSEF) 1.1 控制律形式 1.2 控制量的生成(或者说扰动的补偿) 1.2.1补偿形式① 1.2.1补偿形式② 2.仿真分析 2.1仿真模型 2.2仿真结果 前言 前面的两篇博客依次介绍了TD微分跟踪器安排过渡过程、扩张观测器&#xff1a; 自抗扰…

用腾讯云同步Obsidian笔记

介绍 之前用gitee同步OB笔记&#xff0c;同时做图床。但由于git系产品设置起来相对复杂&#xff0c;且后续可能有外链过审等问题。周五被同事小姐姐安利了用腾讯云COS&#xff0c;试了一下&#xff0c;果然不错。其主要优点如下&#xff1a; 设置简单&#xff0c;学习成本低&…

WSL(Windows Subsystem for Linux)

一、WSL优势 •传统方式&#xff1a;获取Linux操作系统环境&#xff0c;必须安装完整的虚拟机&#xff0c;如VMware•WSL&#xff1a;以非常轻量化的方式&#xff0c;得到Linux系统环境总结&#xff1a;WSL更方便&#xff0c;简单、好用、轻量化、省内存 二、什么是WSL ①不…

Go语言基础(十五):垃圾回收机制(三色标记)

文章目录一、标记清除&#xff08;三色标记&#xff09;大致原理1、标记细节2、root对象二、垃圾回收触发机制垃圾回收&#xff08;Garbage Collection&#xff09;&#xff0c;是一种自动管理内存的机制。传统编程语言&#xff08;如C/C&#xff09;需要开发者对无用内存资源进…

Android实例仿真之三

目录 四 Android架构探究 五 大骨架仿真 六 Android实例分析思路拓展 四 Android架构探究 首先&#xff0c;Android系统所带来的好处&#xff0c;就在于它本身代码的开放性&#xff0c;这提供了一个学习、借鉴的平台。这对分析仿真而言&#xff0c;本身就是一大利好&#xf…

[JAVA安全]Spring Messaging之CVE-2018-1270

漏洞简介 Spring 框架中通过spring-messaging 模块来实现 STOMP &#xff08;Simple Text-Orientated Messaging Protocol&#xff09;,STOMP是一种封装 WebSocket的简单消息协议。攻击者可以通过建立WebSocket连接并发送一条消息造成远程代码执行&#xff0c; spring-messagin…

C++笔记 模板的进阶知识

目录 1. 非类型模板参数 2.模板的特化 2.1 函数模板的特化 2.2 类模板的特化 2.2.1 全特化 2.2.2 偏特化 3.模板的分离编译 3.1 什么是分离编译&#xff1f; 3.2 模板的分离编译 4.模板的总结 模板的初阶内容&#xff1a;(594条消息) C模板的原理和使用_全貌的博客-CSD…

【干货】如何打造HR无法拒绝的简历?测试开发大牛带手把手你写简历!

通过率90%&#xff0c;优秀的软件测试简历长什么样&#xff1f; 也许口才好的人会觉得简历不重要&#xff0c;能说就行了&#xff0c;那是因为你没有体会过石沉大海的感觉&#xff01; 很多人觉得疑惑&#xff0c;为什么我投了那么多简历&#xff0c;都没有接到面试通知&…

加油站会员管理小程序实战开发教程15 完结篇

这篇是本次实战课程的最后一篇,我们在上篇还有两个问题没解决。一个是会员卡类型显示不对,一个是不同的会员卡我们希望背景色显示不同。我们先处理一下这两个问题 1 显示会员卡类型 在列表上直接显示会员卡类型,目前显示的是数字,这个是因为枚举类型导致的。枚举类型在数…

关于vb6无边框的窗体开发遇到的部分问题的解决方案

为了美化或自定义窗体&#xff0c;通常会把窗体做成无边框形式&#xff0c;使用无边框窗体会存在一些问题的&#xff0c;以vb为例&#xff0c;当窗体borderstyle设置为0时&#xff0c;会出现以下问题:1.任务栏不再出现程序图标 2.窗体无法拖行移动 3.窗体无法拖动缩放&#…