函数解析|memset()函数的原理
文章目录
- 1.函数原理
- # 两个特例
- 如何将int类型的数字赋值为1 ?
- 2.日常方法
- 2.1初始化字节
- 2.2 初始化其他数据类型
- 2.3 初始化结构体
- 竞赛中Memset中无穷大常量的设定技巧
1.函数原理
在初识memset函数中,我们简单提到了memset函数引用的是
string.h 头文件,从这里我们可以看出,这是一个为字符类型设置的函数,那么他是怎么实现的?
先看一下源码
void *(memset)(void *s, int c, size_t n)
{ const unsigned char uc = c; unsigned char *su; for (su = s; 0 < n; ++su, --n) *su = uc; return (s);
}
并且我们在前文中知道,memset函数每次是以 一个字节为单位来进行赋值的,而不是一次性赋值4/8个字节,那么问题来了,当我们以int为单位的时候,它究竟是怎样进行的?
举个例子:
在素数筛中我们使用了 memset(arr,1,sizeof(arr));
来对数组进行初始化, 但是 arr的类型如果没有bool类型,而是int类型,那么就会导致一个结果,就是在以字节赋值的时候,int 类型每次调用4个字节(32bit),他会将32bit 分为4*8个bit,每次将最低的bit位进行赋值
内存情况:
所以导致了出现
使得二进制数变为
实际的结果->00000001 00000001 00000001 00000001
想要的结果->00000000 00000000 00000000 00000001
很明显与我们想要赋值的1, 也就是00000000 00000000 00000000 00000001
是不匹配的,如果换算为10进制是一个非常大的值(16843009).是错误的赋值方法。
# 两个特例
但是当memset()刷内存为 0 和-1的时候
答案是正确的,为什么可以正确赋值0和-1 ?
0:八位全零填充四次,得到32位的零,还是零,赋0成功 这个很简单
-1:-1的低八位二进制码为11111111,填充四次,int类型还是-1,赋-1成功。
当进行存放之后,
补码->11111111 11111111 11111111 11111111
根据原反补码之间的关系
我们可以知道 他的原码 10000000 00000000 00000000 00000001 也就是-1
如何将int类型的数字赋值为1 ?
memset(,0xff,sizeof()),0xff转为二进制11111111,int为4字节所以最后为11111111111111111111111111111111为-1。(化为二进制补位,然后再赋值)。
2.日常方法
2.1初始化字节
char data[10];
memset(data, 1, sizeof(data)); // right
memset(data, 0, sizeof(data)); // right
2.2 初始化其他数据类型
int data[10];
memset(data, 0, sizeof(data)); // right
memset(data, -1, sizeof(data)); // right
memset(data, 1, sizeof(data)); // wrong, data[x] would be 0x0101 instead of 1
2.3 初始化结构体
struct sample_struct{char csName[16];int iSeq;int iType;};struct sample_strcut stTest;//一般情况下,清空stTest的方法:stTest.csName[0]='/0';stTest.iSeq=0;stTest.iType=0;//用memset就非常方便,明显优于for循环memset(&stTest,0,sizeof(struct sample_struct));//如果是数组:struct sample_struct test[10];memset(test,0,sizeof(struct sample_struct)*10);
竞赛中Memset中无穷大常量的设定技巧
如果问题中各数据的范围明确,那么无穷大的设定不是问题,在不明确的情况下,很多程序员都取0x7fffffff
作为无穷大,因为这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff
确实是一个完美的选择,但是在更多的情况下,0x7fffffff
并不是一个好的选择。
很多时候我们并不只是单纯拿无穷大来作比较,而是会运算后再做比较,例如在大部分最短路径算法中都会使用的松弛操作:
if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
我们知道如果u,v之间没有边,那么w[u][v]=INF
,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数,我们的松弛操作便出错了,更一般的说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”,它变成了一个很小的负数。
除了要满足加上一个常数依然是无穷大之外,我们的常量还应该满足“无穷大加无穷大依然是无穷大”,至少两个无穷大相加不应该出现灾难性的错误,这一点上0x7fffffff依然不能满足我们。
所以我们需要一个更好的家伙来顶替0x7fffffff,最严谨的办法当然是对无穷大进行特别处理而不是找一个很大很大的常量来代替它(或者说模拟它),但是这样会让我们的编程过程变得很麻烦。在我读过的代码中,最精巧的无穷大常量取值是0x3f3f3f3f,我不知道是谁最先开始使用这个精妙的常量来做无穷大,不过我的确是从一位不认识的ACMer(ID:Staginner)的博客上学到的,他/她的很多代码中都使用了这个常量,于是我自己也尝试了一下,发现非常好用,而当我对这个常量做更深入的分析时,就发现它真的是非常精巧了。
0x3f3f3f3f
的十进制是1061109567,也就是10 ^ 9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134
,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的初始化
),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f
,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。
所以在通常的场合下,0x3f3f3f3f真的是一个非常棒的选择。
函数解析|memset()函数的原理相关推荐
- 【note】fill函数和memset函数的区别和使用
memset函数 按照字节填充某字符 在头文件<cstring>里面 fill函数 按照单元赋值,将一个区间的元素都赋同一个值 在头文件<algorithm>里面 注意: fi ...
- 如何给数组用fill函数和memset函数给数组赋初值
fill是按照单元来赋值的,所以可以填充一个区间的任意值 #include<iostream> #include<stdio.h> #include<string.h&g ...
- 取整函数,back函数,memset函数,sizeof函数,--LeetCode刷题笔记3
1. c++的取整函数,题目:2020春季赛,拿硬币 cnblogs.com/zjutlitao/p/3558218.html 头文件:<cmath> fix,朝零方向取整,如fi ...
- strlen函数,strcpy函数,strcat函数,memset函数,strcmp函数,memcpy函数,memove()函数
文章目录 一,strlen函数(库函数头文件<string.h>) 二,strcpy函数(库函数头文件<string.h>)拷贝函数 三,strcat函数(头文件是<st ...
- malloc函数及memset函数用法详解
最近在力扣刷题时,发现普通的创建数组很容易引起编译器报错. 而使用到动态内存分配malloc()函数和初始化函数memset()可以避免这类问题的发生.本文将详细介绍一下两个函数的 malloc() ...
- python sizeof函数_sizeof运算符和strlen函数 ZeroMemory函数和memset函数
strlen和sizeof 在之前一直把两个东西在某种意义上混淆了,对它们的功能不是很熟悉. 在逛了下谷歌之后,发现自己误解的东西很多. strlen与sizeof的区别 1.sizeof操作符的结果 ...
- memset linux 头文件,C函数之memset()函数用法
1.功能:将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为指向S的指针 2. 需要的头文件 o ...
- fill函数与memset函数比较
[C++]fill函数,fill与memset函数的区别 1,memset函数 按照字节填充某字符 在头文件< cstring >里面 2,fill函数 按照单元赋值,将一个区间的元素都赋 ...
- memcmp函数和memset函数的使用
int memcmp(const void* ptr1,const void* ptr2,size_t num) *比较从ptr1和ptr2指针开始的num个字节 *ptr1大于ptr2时返回1,等于 ...
最新文章
- MVC实现简单的上传功能
- 【操作系统】【C/C++开发】内存管理
- 大学计算机基础课程报告python-大学计算机基础
- 【活动(广州)】office365的开发者训练营
- kafka监控工具kafkaOffsetMoniter的使用
- .Net 高效开发之不可错过的实用工具(转载)
- Elasticsearch教程-从入门到精通-ES索引迁移
- 有了设计类导航网站,从此就不再需要瞎找了
- java语言cd_java语言
- An Objective-C Error
- Linux第7章Gdk及Cairo基础,GNOME 平台的2D图形编程(GTK,GDK,Cairo...) 简介 [转]...
- ubuntu搭建Fabric环境
- java多线程12:阻塞队列Queue
- 统计推断(二) Estimation Problem
- 惠普win10一键还原_惠普win10一键还原怎么用 - 卡饭网
- Eclipse BIRT使用之BIRT Designer篇(转)
- python3 pdf 转 txt
- 储存器RAM、Flash、ROM、HHD简明对比
- 如何在MySQL中实现替换字段部分内容
- 增加samba用户提示Failed to add entry for user
热门文章
- 快递 E 栈系统(控制台简易版)
- ElasticSearch的几种检索方式
- 小提琴和钢琴一起学行吗_关于学习钢琴和小提琴的多方面对比,比完你可能要吓一跳...
- 海康威视SDK控制台程序consoleDemo
- 【转】VCM驱动IC--close loop
- 微软免费的远程桌面管理工具,RDCMan——系统运维不可缺的软件
- 【视频版】《Easy搞定Golang设计模式》
- 滑动(左滑右滑加载下一页)
- python单词表-杨鹏记单词法安排表自动生成算法(Python实现)
- 利用python + pyecharts+Pandas对北上广深等城市进行租房数据分析