函数解析|memset()函数的原理

函数解析|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]

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真的是一个非常棒的选择。

相关推荐

DNF男大枪速射重炮套装简评
365bet电子游戏

DNF男大枪速射重炮套装简评

🗓️ 07-16 👁️ 973
剑灵疾风武器怎么获取 剑灵剑士武器外观排行 知识库
365bet电子游戏

剑灵疾风武器怎么获取 剑灵剑士武器外观排行 知识库

🗓️ 07-27 👁️ 9369
1万毫安电池智能手机 欧奇黑牛P6 PRO亮点解析
365赢多少钱会被限额

1万毫安电池智能手机 欧奇黑牛P6 PRO亮点解析

🗓️ 07-25 👁️ 4184
总裁颤音:女人,哪里跑/撒旦总裁的独宠玩具
BT365软件提现不了

总裁颤音:女人,哪里跑/撒旦总裁的独宠玩具

🗓️ 07-08 👁️ 1741
揭秘手机上查看QQ年龄的方法,不再被年龄限制困扰!(手机在哪里看qq年龄)
一加5 8GB+128GB参数
365赢多少钱会被限额

一加5 8GB+128GB参数

🗓️ 06-29 👁️ 790