C/C++ memset方法的误区

 更新时间:2021年04月15日 09:05:17   作者:李春港  
memset 作为对内存初始化的函数,还是有不少坑和误区的,今天就来对这个函数作一个总结。避免后期使用不当踩入坑,需要的朋友可以参考下

一、函数作用

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

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 接口清零 和 调用循环进行清零,进行一个测试后如下:

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

模式 memset for
debug 375ms 2156ms
release 343ms 329ms

  • 因为 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;
  • 为什么呢?
  • 我们把这个数组转换成二进制,得到:
  • ( 00000101 00000101 00000101 00000101 ) 2 (00000101 \ 0000 0101 \ 0000 0101 \ 0000 0101)_2 (00000101  00000101  00000101  00000101)2
  • 因为 i n t int int 占据了 4 4 4 个字节,把每个字节都设置成了5,所以最后转成十进制就变成了 84215045;
  • 同理,当类型是 short(二字节整数),或者 long long(八字节整数)都会有类似问题,总结表格如下:

总结表格如下:

 

memset值 char short int long long
0 0 0 0 0
-1 -1 -1 -1 -1
5 5 1285 84215045 361700864190383365

  • 表格中,只有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 取首地址

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

const int maxn = 1024;
int *p = new [maxn];
memset(p, 0, sizeof(p));
  • 这里进入了另一个误区,因为 p p p 在这里虽然是数组首地址,但是它扮演的角色更多的,其实是个指针,所以在进行 sizeof 运算符操作的时候,取得的值并不是 4096,而是指针的大小;
  • 32位机子上,指针大小为4,;64位机子上,指针大小为 8;
  • 正确做法是:
const int maxn = 1024;
int *p = new [maxn];
memset(p, 0, maxn * sizeof(int));

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

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

void fun(int a[maxn])
{    
    memset(a, 0, sizeof(a));
}
  • 这里调用同样是错误的,因为当数组作为传参的时候,这里的 a 已经退化为指针,所以同样不能用 sizeof 数组首地址来取大小;
  • 正确做法是:
void fun(int a[maxn]) 
{    
    memset(a, 0, maxn * sizeof(int));
}

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

参考于:CSDN-英雄哪里出来https://blog.csdn.net/WhereIsHeroFrom/article/details/111660632

到此这篇关于C/C++ memset方法的误区的文章就介绍到这了,更多相关C++ memset方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++控制台版扫雷游戏

    C++控制台版扫雷游戏

    这篇文章主要为大家详细介绍了C++控制台版扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 使用用C++做一颗会跳动的爱心实例代码

    使用用C++做一颗会跳动的爱心实例代码

    大家好,本篇文章主要讲的是使用用C++做一颗会跳动的爱心实例代码,感兴趣的同学赶快来看一看吧,欢迎借鉴学习C++做一颗会跳动的爱心实例代码
    2021-12-12
  • C++11中使用using的方法

    C++11中使用using的方法

    在 C++ 中 using 用于声明命名空间,使用命名空间也可以防止命名冲突。这篇文章主要介绍了C++11中使用using的方法,需要的朋友可以参考下
    2021-09-09
  • c++ 封装一个截图服务

    c++ 封装一个截图服务

    这篇文章主要介绍了c++ 封装一个截图服务的方法,帮助大家更好的理解和使用c++,感兴趣的朋友可以了解下
    2021-01-01
  • C++实现LeetCode(151.翻转字符串中的单词)

    C++实现LeetCode(151.翻转字符串中的单词)

    这篇文章主要介绍了C++实现LeetCode(151.翻转字符串中的单词),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • ​​​​​​​C语言实现单链表基本操作方法

    ​​​​​​​C语言实现单链表基本操作方法

    这篇文章主要介绍了​​​​​​​C语言实现单链表基本操作方法,文章围绕主题展开详细介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-05-05
  • C++实现LeetCode(107.二叉树层序遍历之二)

    C++实现LeetCode(107.二叉树层序遍历之二)

    这篇文章主要介绍了C++实现LeetCode(107.二叉树层序遍历之二),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 详解c语言实现的内存池(适用于两个线程、不加锁、效率高)

    详解c语言实现的内存池(适用于两个线程、不加锁、效率高)

    这篇文章主要介绍了c语言实现的内存池(适用于两个线程、不加锁、效率高),设计一个内存池,要求效率比系统调用的效率要高(测试1万次),同时支持一个线程申请,另外一个线程释放,需要的朋友可以参考下
    2024-02-02
  • __stdcall 和 __cdecl 的区别浅析

    __stdcall 和 __cdecl 的区别浅析

    __stdcall 和 __cdecl 的区别浅析,需要的朋友可以参考一下
    2013-03-03
  • C语言函数调用基础应用详解

    C语言函数调用基础应用详解

    函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写大量重复的代码。这篇文章主要介绍了c语言是如何处理函数调用的?需要的朋友可以参考下
    2023-02-02

最新评论