C++ memset 踩坑

2021/5/15 20:25:30

本文主要是介绍C++ memset 踩坑,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、前言

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

二、函数作用

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

67b4a8604c1d86d050bbea0c5ca99c20.png

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

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

三、效率对比

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

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

1128fc8bb983ef1796977454b0e47970.png

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

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

四、误区总结

1、按字节设置

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

c880a5a6ad0baedf39ff8c0f1e03f829.png

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

c2dac995eaef2efe96bc1cca3ee4aa2a.png

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

为什么呢?

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

4229d1b9729225386d52b26a33d54ebc.png

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

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

b2aa977ce0d9c3277b7db25d1a5f484f.png

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

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

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

570195c747932a63a036a45b395c3b81.png

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

d94ec84ef5c02456d0cf05a47e5998c0.png

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

在堆上申请了一个数组空间,并且想要给它初始化,调用如下:

05ffcab5c496efb34a577ddfae343972.png

这里进入了另一个误区,因为 p pp 在这里虽然是数组首地址,但是它扮演的角色更多的,其实是个指针,所以在进行 sizeof 运算符操作的时候,取得的值并不是 4096,而是指针的大小;

32位机子上,指针大小为4,;64位机子上,指针大小为 8;

正确做法是:

42bb1cc58f5c1bc0d7523e154e9051eb.png

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

对传参为数组的数据进行 memset,调用如下:

ec7fe9dfcf9577a67d95bec8b7c02e5c.png

这里调用同样是错误的,因为当数组作为传参的时候,这里的 a 已经退化为指针,所以同样不能用 sizeof 数组首地址来取大小;

正确做法是:

410664dd1befcbda4870685dbde637b2.png

当然,当传参是结构体指针的时候也是如此;





这篇关于C++ memset 踩坑的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程