搜索
热搜: 活动 交友 discuz
Hi~登录注册
查看: 2697|回复: 0

慎用memset

[复制链接]

26

主题

26

帖子

106

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106
发表于 2021-9-17 12:48:21 | 显示全部楼层 |阅读模式
C/C++的内存初始化函数memset函数一定要慎用

memset是一个极其方便的函数,其主要功能是初始化一段内存区域,来自于头文件<string.h>,但是这个函数也是一个相对而言很容易出错的函数,让我们先来看一下他的解释:

C++ Reference对于memset的定义为:
Fill block of memory
Sets the first num bytes of the block of memory pointed by ptr to the specified value (interpreted as an unsigned char).
void memset ( void ptr, int value, size_t num );
ptr: Pointer to the block of memory to fill.
value: Value to be set. The value is passed as an int, but the function fills the block of memory using the unsigned char conversion of this value.
num:Number of bytes to be set to the value.
解释一下其中的三个参数:
Ptr表示一个指针,其指向一段内存
Value表示填充这个区域内存的值
Num表示这段内存区间
这里可以注意一下,我们通常使用sizeof进行num段的表示,sizeof可以快速的获取到所需空间的字节大小,这通常是与类型所决定的。
比如说我们可以给一个数组初始化为0,测试代码如下:
  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. const int maxn=20;
  5. int a[maxn];
  6. int main(){
  7.     cout<<"memset 使用前:"<<endl;
  8.     for(int i=0;i<maxn;i++){
  9.         cout<<a[i]<<' ';
  10.     }
  11.     cout<<endl<<"memset 使用后:"<<endl;
  12.     memset(a,0,sizeof(a));
  13.     for(int i=0;i<maxn;i++){
  14.         cout<<a[i]<<' ';
  15.     }
  16.     cout<<endl;
  17.     return 0;
  18. }
复制代码

结果为:
memset 使用前:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
memset 使用后:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
这样乍一看没什么问题,而且我们的数组在创建的同时自己就已经是一个赋值为0的情况下了,所以这样的操作没有任何问题,我们再拿字符数组进行一下测试,修改代码如下:


  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. const int maxn=20;
  5. int a[maxn];
  6. int main(){
  7.     cout<<"memset 使用前:"<<endl;
  8.     for(int i=0;i<maxn;i++){
  9.         cout<<a[i]<<' ';
  10.     }
  11.     cout<<endl<<"memset 使用后:"<<endl;
  12.     memset(a,'A',sizeof(a));
  13.     for(int i=0;i<maxn;i++){
  14.         cout<<a[i]<<' ';
  15.     }
  16.     cout<<endl;
  17.     return 0;
  18. }
复制代码

结果为:
memset 使用前:
memset 使用后:
A A A A A A A A A A A A A A A A A A A A
因为我们的字符数组在空的情况下对于ASCII码表格的空,因此直接进行输出是没有任何东西的,但是我们在使用memset函数进行集体初始化之后他就变得有了值,如本样例他会变成全部是’A’的内容。
接下来就是问题所在了
如果我们想要将其全部初始化一个自定义的值呢?比如说全部初始化为1,又会怎么样呢?
我们修改测试代码如下:
  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. const int maxn=20;
  5. int a[maxn];
  6. int main(){
  7.     cout<<"memset 使用前:"<<endl;
  8.     for(int i=0;i<maxn;i++){
  9.         cout<<a[i]<<' ';
  10.     }
  11.     cout<<endl<<"memset 使用后:"<<endl;
  12.     memset(a,1,sizeof(a));
  13.     for(int i=0;i<maxn;i++){
  14.         cout<<a[i]<<' ';
  15.     }
  16.     cout<<endl;
  17.     return 0;
  18. }
复制代码

其结果为:
memset 使用前:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
memset 使用后:
16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009
这与我们所求的结果大相径庭,我们所需要赋值的1去哪了?为什么是16843009这个完全不相干的数字在里面,难道我们的代码出错了么?为了测试,我们又将其内容赋值为了2,结果所有的数据变成了33686018这个值,同样与我们所需求的结果不相同。
这是为什么呢
其原因就是出在我们memset的底层设计上,memset是采用每一个字节每一个字节进行赋值的,也就是说,我们所给出的1,其因为int类型占用四个字节的原因被按照内存分为了四个部分,每一个部分存在一个1,因此经过memset函数初始化之后的数组的内容应该成了这个样子(使用二进制进行表示)
0000 0001 0000 0001 0000 0001 0000 0001 共4个字节,每一个字节占用8个二进制位
我们再来计算一下,
为了方便理解,这里不写直接的运算函数,而是使用C++的bitset函数进行测试,我们直接把数据放入bitset函数中,看一下他转换出来的二进制是否与我们之前的预期相同即可。
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4.     bitset<32> s(16843009);
  5.     cout<<s<<endl;
  6.     return 0;
  7. }
复制代码

其结果为:
00000001 00000001 00000001 00000001
(为了方便而直观的表示我稍微用了一下空格做处理,8个二进制一组)
完全一致,因此我们根据这个结论,得出了一个经验:
  • 对于整形等精度在2字节以上的数据类型,除了赋值为0 (十六级进制0x00)与赋值为-1 (十六级进制0xff)之外的数据,不方便进行直接的初始化的结论。
  • 对于字符数组这样的单字节数据类型,可以方便的进行初始化
  • 此外,memset相当于一次从头到尾的遍历,其本身的时间复杂度是O(N)线性级别的,与for()循环遍历整个数组并且依次赋值并没有其他的区别,如果在极其苛刻的时间复杂度条件下这个函数需要慎用。
回复

使用道具 举报

游客
回复
您需要登录后才可以回帖 登录 | 立即注册

快速回复 返回顶部 返回列表