memset的坑

news2025/7/2 17:57:26

前言

memset 作为对内存初始化的函数,还是有不少坑和误区的,今天就来对这个函数作一个总结。

一、函数作用


memset 函数在 C++ 中被广泛应用于内存的初始化和设置。它可以将一段连续的内存空间快速设置为指定的值。这个函数主要作用于数组、结构体等数据类型,使其初始状态满足特定的要求。

最简单的调用是将一个数组清零,代码示例如下:

const int maxn = 1024;
int a[maxn];
memset(a, 0, sizeof(a)); // a[0]=a[1]=a[...]=0;

这里,sizeof(a) = maxn * 4 = 4096;

表示的是将 数组首地址 a 开始往后的 4096 个字节,都设置为 0

换句话说,通过 memset 函数,我们可以将数组 a 的所有元素都初始化为 0。这种操作在处理大量数据时,可以大大提高编程效率。

二、效率对比


直接调用 memset 接口清零 和 调用循环进行清零,进行一个测试后如下:

对长度为 10000000 的数组,执行100次调用;

因为 release 版本会做各种优化,编译器发现重复执行无效逻辑就会跳过,所以不太好造数据测试,研究时间效率的时候还是参考 debug 版本(当然,软件发布的时候肯定用的是 release 版本)。

memset 无论从时间效率,还是代码整洁来看都是由于 for 循环的,当然也带来了一些容易引起误解的地方。

三、误区总结

1、按字节设置

memset 实现原理是根据字节来设置的,比如对于字节数组 char a[100],将所有字节都设置为5,就可以调用:

memset(a, 5, sizeof(a));

但是,对于 int b[100],也采用这种方法,就会导致错误:

memset(b, 5, sizeof(b));

得到 b 数组中元素的值为 84215045;

为什么呢?

我们把这个数组转换成二进制,得到:

因为 int 占据了 4 个字节,把每个字节都设置成了 5,所以最后转成十进制就变成了 84215045;

同理,当类型是 short(二字节整数),或者 long long(八字节整数)都会有类似问题,总结表格如下:

表格中,只有0 和 -1是正常的,因为 0 的二进制表示中,所有位都为0;-1 的二进制表示中,所有位都为 1;

特别的,当需要设置的数,对应类型的每个字节都是同一个数的时候,也可以采用 memset,比如:int 类型的 252645135(十六进制表示为:0x0f0f0f0f);

2、设置的值只有最低字节有效

memset(a, 0x05ffffff, sizeof(a));

memset(a, 0xffffff05, sizeof(a));

memset(a, 0xffffff08, sizeof(a));

memset(a, 0x12345678, sizeof(a));

设置值的时候,只会采用最低的字节作为赋值用,通俗的讲,就是以上四句话调用,等价于:

memset(a, 0xff, sizeof(a));

memset(a, 0x05, sizeof(a));

memset(a, 0x08, sizeof(a));

memset(a, 0x78, sizeof(a));

3、堆内存不可直接 sizeof 取首地址

在 C++ 中,当我们在堆上申请内存时,通常使用 new 关键字。当我们使用 sizeof 运算符对一个指针进行操作时,返回的将是指针本身的大小,而不是它所指向的内存的大小。因此,当我们需要对一个数组或结构体指针进行初始化或内存设置操作时,我们需要正确计算内存大小。以下是关于堆内存和数组参数传递时的一些建议和注意事项。

  1. 在堆上申请内存时,使用 new[] 操作符来创建一个动态数组。这将返回一个指向数组第一个元素的指针。例如:
const int maxn = 1024;
int *p = new int[maxn];

  1. 当需要对动态数组进行初始化或内存设置操作时,使用 sizeof 运算符与数组元素类型的大小相乘,以获取正确的内存大小。例如:
const int maxn = 1024;
int *p = new int[maxn];
memset(p, 0, maxn * sizeof(int));

4、传参数组不可直接 sizeof 取首地址

  1. 当将数组作为函数参数传递时,数组名会退化为指针。因此,在函数内部,我们不能使用 sizeof 运算符直接获取数组大小。相反,我们需要将数组大小作为函数参数传递,或者使用其他方法来获取数组大小。例如:
void fun(int a[], int size) {
    memset(a, 0, size * sizeof(int));
}

  1. 当结构体指针作为函数参数传递时,同样需要注意这个问题。结构体指针会退化为指针,因此我们不能使用 sizeof 运算符直接获取结构体的大小。解决方法与数组类似,将结构体大小作为函数参数传递,或者使用其他方法获取结构体大小。例如:
void fun(MyStruct *s, int size) {
    memset(s, 0, size * sizeof(MyStruct));
}

总之,在处理堆内存和数组参数传递时,我们需要注意指针和数组名之间的差异,并使用正确的方法获取内存大小,以避免错误和潜在的内存问题。

本文由mdnice多平台发布

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

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

相关文章

python 安装、配置、使用 xlrd模块

xlrd模块的分为python安装 和pycharm配置两个步骤 1. 安装xlrd模块 打开cmd,输入 pip install xlrd 按 enter键 安装完成即可,如果想升级,根据提示完成即可 2. pycharm 配置xlrd pycharm模块导入xlrd模块时,import xlrd #导入模…

【前端知识】JavaScript——var 与 let 的区别

【前端知识】JavaScript——var 与 let 的区别 var声明的变量会自动提升到函数作用域顶部,而let不会。 在解析代码时,JavaScript 引擎会注意出现在块后面的 let 声明,只不过在此之前不能以任何方式来引用未声明的变量。在 let 声明之前的执行…

基于simulink的DPLL仿真笔记

该笔记主要用于本人思路整理与记录 本设计运用的是电荷泵一阶环路滤波器,二阶三阶则在此基础上举一反三,以后如有机会会慢慢补全 文章目录 一.仿真模型PS(题外话) 二.仿真结果三.环路滤波器分析1. 环路滤波器对比LPF2. 环路滤波器…

(阿里云)STM32L+BC20+MQTT协议传输温湿度,ADC,电压,GPS数据到阿里云物联网平台

1、材料准备 准备以下材料 2、设备连接 2.1 插入物联网卡 首先把BC20核心板从开发板上拆下来 然后将物联卡放置在BC20核心板内 物联卡放置完成将BC20核心板重新插入到开发板内(注意不要弄错方向) 同时接入天线 2.2 连接ST-Link仿真器 用3条杜邦线接…

mzjh 项目鉴权

获取后端的接口后 将后端的数据转成数组 并报错保存 业务逻辑 function getPoints(menus) {let Points []for (let menu of menus) {if (menu.useType 2) {if (menu.grantName ! undefined && menu.grantName ! null) {Points.push(menu.grantName)}}if (menu.child…

TCP的窗口控制和重发控制【TCP原理(笔记三)】

文章目录 利用窗口控制提高速度窗口控制与重发控制确认应答未能返回的情况某个报文段丢失的情况 控制流 利用窗口控制提高速度 TCP以1个段为单位,每发一个段进行一次确认应答的处理,如图。这样的传输方式有一个缺点。那就是,包的往返时间越长…

YOLO-V5分类实战系列 —— 调优自己的数据集+RK1808部署

YOLO-V5分类实战系列 —— 调优自己的数据集 1、保存训练和测试图片2、数据归一化3、数据增强3.1、数据增强库:albumentations3.2、数据增强库:torchvision 4、ONNX CPU 推理4.1、Pt 模型转为 ONNX4.2、ONNX 推理验证4.3、 ONNX CPU推理(C&am…

理解Deformable Convolution网络

1.简介 偶然了解到了可形变卷积这篇文章,看了几篇博文后大致了解的差不多了,但是有些细节还是看了代码之后才理解。这里想自己写一下关于这篇论文的了解,希望自己能够讲清楚。这里放一篇写的很好地博文链接,想更深入了解代码的可…

Gateway自定义过滤器——全局过滤器

一、什么是全局过滤器🍉 首先,我们要知道全局过滤器其实是特殊路由过滤器(特殊的GatewayFilter),会有条件地作用于所有路由。 为什么要自定义全局过滤器?就好比是看大门的保安大叔,平时主要是做好进出大门外来人员登记…

Chatglm实现agent控制

背景: 这个系列文章,会从LLM搭建应用生态角度来写。从0到1训练一个大的通用的模型对于大部分人和团队来讲是不现实的。重资金,重技术含量、重投入这几个门槛可以把很多团队直接劝退。那么在LLM蓬勃发展的时候我们可以做些什么呢,…

C语言程序设计——字符、字符串、内存函数

一、长度不受限的字符串函数 1. strlen size_t strlen(const char* str); 功能:求字符串长度 (1)字符串以\0作为结束标志,strlen函数返回的是在字符串中\0之前出现的字符个数(不包含\0)。 &#xff08…

【每日运维】大文件的分割与合并

产生背景 特殊单位需要将文件刻盘带入,并且刻盘有大小限制,所以有了这个需求 推荐方法 个人电脑上使用 split 命令指定大小分割Linux 服务上使用 cat 命令进行合并使用 md5sum 命令校验包的完整性 方法演示 需要将一个按照100M分割后刻盘导入 在个…

数据结构 ~ 树

什么是树 - tree 一种分层数据的抽象模型; 如:DOM、级联选择、树形控件,js 中没有树 可以用 Object 构建树: const tree {val: a,children: [{val: a-1,children: [{val: a-1-1,children: []}]},{val: a-2,children: [{val: a…

mapbox绘制多边形

1、实现效果 请忽略马赛克 2、实现思路 绘制一个填充的多边形,再描个边框。 3、实现代码 绘制多边形函数 drawPolygon() {map.addSource(maine, {type: geojson,data: https://asc-test1.oss-cn-beijing.aliyuncs.com/2023/07/05/45f6bd80f2f34d79b3e457b31ec5d…

云原生网关如何实现安全防护能力

作者:刘晓瑞(钰诚) 云原生网关:将安全、流量和微服务三合一 作为面向南北向的公网网关,使用 Waf 防护异常流量是很常规的需求,而且随着互联网环境变得越来越复杂,用户对防护的诉求是持续增强的,常规做法是…

需要我怎么帮你?关于维护Nutsdb开源社区的思考

背景 近来有人问我打算怎么继续维护Nutsdb社区,他们当中不乏有开源项目的狂热爱好者,发起了好几个几千star的开源项目,也有对Nutsdb感兴趣的,想来问问后续的计划。这不禁让我回想到从刚开始参与这个项目到最近这段时间一个人维护…

三维重建的工作

文章目录 一、北京:二、广州:三、深圳: 一、北京: 链接 三维重建技术是自动驾驶领域4D真值数据生成的核心基础能力。融合LiDAR、Camera、IMU、轮速计等传感器数据像BlockNeRF一样重建城市级别逼真精细的三维场景,将…

ArcGIS Pro 矢量数据的空间校正

GIS 数据通常来自多个源。当数据源之间出现不一致时,有时需要执行额外的工作以将新数据集与其余数据进行整合。相对于基础数据而言,一些数据会在几何上发生变形或旋转。 在编辑环境中,空间校正提供用于对齐和整合数据的交互式方法。 空间校正可执行的一些任务包括:将数据…

SQLite数据库安装

安装方式一: sudi apt-get install sqlite 安装方式二: https://www.sqlite.org/download.html 1. 把下载的文件 sqlite-autoconf-3420000.tar.gz 上传到开发板 2. tar xvf sqlite-autoconf-3420000.tar.gz 解压 3. cd sqlite-autoconf-3420000 进入…

[Mysql] 索引失效的情况详解~

一条查询语句走了索引和没走索引的查询效率是非常大的,但有很多情况导致我们的索引失去效果。这里总结一下常见的索引失效的情况~ 数据准备 我们准备一张简单的学生来做演示。 CREATE TABLE student (id int NOT NULL COMMENT id,name varchar(255) COLLATE utf8…