【C-memset】memset函数的深坑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qilei2010/article/details/88564161

一、引子


int a[4];
memset(a, 1, 4);

你觉得 a[0] 应该是多少?

你绝对想不到,输出是 16843009。

为什么?不应该是 1 么?

二、正文

1、memset函数声明

    memset(void *s,int ch,size_t n);

    将s开始的n个字节用 ch的二进制的后八位 替代并且返回s;并不是简单的将 ch 的值设置到s数组中。一般 ch 只出现 0 和 -1 两种情况,0是将内存全置为0,-1是将内存全都置为 1。其它ch值均不常用。

所以实际 a[0] 的值是 0x01010101 ,二进制就是 00000001 0000001 0000001 0000001。int类型占4个字节,每个字节都被覆盖为0000001了。

2、头文件:string.h\memory.h

3、注意:

  1. memset函数按字节对内存块进行初始化,所以不能用它将int数组初始化为0和-1之外的其他值(除非该值高字节和低字节相同)。
  2. ch接收参数的范围0-255 ,该函数只能取ch的后八位赋给你所指定的范围的每个字节,比如int a[5]赋值memset(a,-1,sizeof(int )*5)与memset(a,511,sizeof(int )*5) 所赋值的结果是一样的都为-1;因为-1的二进制码为(11111111 11111111 11111111 11111111)而511的二进制码为(00000000 00000000 00000001 11111111)后八位都为(11111111),所以数组中每个字节,如a[0]含四个字节都被赋值为(11111111),其结果为a[0](11111111 11111111 11111111 11111111),及a[0]=-1,因此无论ch多大只有后八位二进制有效,而八位二进制的范围是(0~255)。而对字符数组操作时则取后八位赋值给字符数组,其八位值作为ASCII  码。
  3. 注意n和ch的位置
  4. 不要过度使用(ch一般只取 0 和 -1)
  5. 注意有时候的指针降级
int func(struct some *a)
{
   …
   memset(a,0,sizeof(a))
}

    此处由于指针降级导致sizeof(a)返回的是一个指针的字节数,4字节。memset一般就用于初始化内存空间,比起for来,memset是一个比较快捷的方式。

4、更多例子

char a[4];
memset(a, '1', sizeof(a));

上面是正确的例子。这时候 a[0] 是字符 '1'。

当设置 char 类型数组时,ch 可以是0 和 任意的字符,简单说因为字符刚好是一个字节, char类型数组刚好每个元素都是一个字节,正好一一对应。

下面则是错误例子。

int a[4];
memset(a, '1', sizeof(a));

这时候 a[0] 是 825307441。(想不到吧,居然不是字符‘1’的ASCII值49?)

解析:根据ASCII表,单字节字符 '1' 的二进制是 0011 0001,十进制是49,十六进制是 0x31。因为int类型占4个字节,memset函数自觉地依次将a[0]的4个字节设置为0x31,于是四个字节连起来读取,a[0]就是0x31313131,转换为十进制是825307441。

5、总结

使用memset 给 int 类型数组初始化一定要慎重,一般仅用数值 0 和 -1。若要设置其它的值,还是乖乖用 for 循环吧。

-----刷UVa232被坑有感,记录于此。

猜你喜欢

转载自blog.csdn.net/qilei2010/article/details/88564161