使用memset踩过的坑

使用memset踩过的坑

使用memset 踩过的坑

库函数 memset()

​ memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的;包含在<[string.h]>头文件中,可以用它对一片内存空间逐字节进行初始化;

注意: 该函数是按一个字节一个字节来给数组或者是结构体赋值。

原型为 :

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

这里s可以是数组名,也可以是指向某一内在空间的指针;

v为要填充的值;

n为要填充的字节数;

​ 在C++的API函数文档中对该函数的阐述如下:

void * memset ( void * ptr, int value, size_t num );

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`)

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.size_t is an unsigned integral type.

memset()的使用

使用memset()对单字节数据类型参数赋值

例1:

#include

#include

int main ()

{

char str[50];

strcpy(str,"This is string.h library function");

puts(str);

memset(str,'$',7);

puts(str);

return(0);

}

​ 该demo是memset()函数的最基本的用法,即对一个char类型的数组进行赋值。通过编译输出结果如下:

This is string.h library function

$$$$$$$ string.h library function

例2:

uint8_t buffer[10];

uint8_t uSize;

uSize = 0;

memset(buffer,0,10);

uSize = sizeof(buffer);

memset(buffer,0,sizeof(buffer));

​ 该函数是针对uint8_t类型的数据进行赋值,在上述demo中uSize的值位10;因此表达式memest(buffer,0,10)和memset(buffer,0,sizeof(buffer))的功能是一样的。memset除了能够对内存块初始化,也可以通过memset(buffer,1,sizeof(buffer))类似的操作对内存块设置其他的数值。

注意: 在进行非零操作是,注意赋值的范围。

memset()对非单字节数据类型的变量赋值

例3:

uint16_t i;

uint16_t uBuffer[10];

for(i=0;i<10;i++)

{

uBuffer[i] = 100;

}

memset(uBuffer,0,10);

memset(uBuffer,0,sizeof(uBuffer));

memset(uBuffer,0,sizeof(uint16_t)*10);

i = sizeof(uBuffer);

for(i=0;i<10;i++)

{

uBuffer[i] = 100;

}

memset(uBuffer,255,10);

memset(uBuffer,255,sizeof(uBuffer));

memset(uBuffer,255,sizeof(uint16_t)*10);

memset(uBuffer,256,sizeof(uBuffer));

​ demo中的7到9行的本意都是对uBuffer变量进行赋0操作。但是程序实际跑出来输出的效果可能和原来的初衷不同。

​ 首先代码memset(uBuffer,0,10)中设置10表面上看和uBuffer数组大小相等,表面上通过改代码可以将uBuffer数组全部设置成0;但实际上并不是这样,代码执行后uBuffer[0]~uBuffer[4]的值的确是被设置成0,但uBuffer[5]~uBuffer[9]的值是没变的。why? 其原因是memset在对内存操作是以一个字节为单位进行的,长度为10,即对10个字节进行置0操作,而uint16_t类型的数据在内存中是占两个字节大小的,即只完成了5个uint16_t数据类型的操作。

​ 其次,通过sizeof(uBuffer)代码中返回的值也可以看出,整个uBuffer数组的长度其实位20。在内存中uBuffer数组大致的存在方式如下图:

memset(uBuffer,0,sizeof(uBuffer)) 和 memset(uBuffer,0,sizeof(uint16_t)*10)是将uBuffer数组全部赋值为0; 其中sizeof(uint16_t)*10是先获取单个uint16_t的单元大小,然后扩展成整个uBuffer的大小。

​ memset(uBuffer,255,10) 是将uBuffer[0]~uBuffer[4]所在内存赋值为0xFF,即uBuffer[0]的值位0XFFFF,即uBuffer[0] = 65535。而其余的uBuffer[5]~uBuffer[9]的数值保持不变。

​ memset(uBuffer,255,sizeof(uBuffer))和memset(uBuffer,255,sizeof(uint16_t)*10)将所有的uBuffer数组的数据都设置为65535。

​ memset(uBuffer,256,sizeof(uBuffer))代码的意图是将256赋值给uBuffer数组中的每一个元素,但真实情况去不是,该代码存在两个问题首先256并不能真实赋值给uBuffer数组中的每一个元素,其次即时能够赋值给数组中的每个成员,256这个数值也不能赋值给uBuffer。该代码的运行后的结果是uBuffer里面的数组全部为零。原因如下256的十六进制位0x100,而memset中需要填充的值在API中的参数说明中有明确规定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。

```c

char cBuf[10];

char *p = cBuf;

memset(cBuf, 0, sizeof(cBuf));

memset(p, 0, sizeof(str));

memset(cBuf, 0, sizeof§);

memset(cBuf,0,sizeof(*p));

//只能写sizeof(cBuf), 不能写sizeof§

```

​ memset(cBuf, 0, sizeof(cBuf))和memset(p, 0, sizeof(str)) 将cBuf中的数据设置为零,而memset(cBuf, 0, sizeof§)只能将cBuf数组中的前四个设置为0,原因是因为sizeof()指针变量,其结果位4,虽然p也指向数组的起始地址,但该代码并不能实现对数组全部赋值为零。memset(cBuf,255,sizeof(*p))只能将cBuf[0]设置为零。原因是sizeof(*p)的长度为1;

​ 针对其他数据类型的变量,其规则和uint16_t一致。

memset()对结构体的变量赋值

struct sData_t

{

uint16_t year;

uint8_t month;

uint8_t day;

};

一般对于结构体进行清空的方式有以下几种方式:

在一个变量一个变量的清空:

sData_t sData;

sData.year =0;

sData.month =0;

sData.day =0;

直接用memset()清空:

memset(&sData,0,sizeof(sData_t));

针对一般的结构体中可以采用上述的memset()进行清空操作,但对于结构体中含有指针类型的变量时,还是建议使用一个个半两单独赋值。

struct sData {

int x;

int* p;

};

sData sDataPar;

sDataPar.p = new char[10];

memset(&sDataPar, 0, sizeof(sDataPar));

​ 当memset初始化时,并不会初始化p指向的char数组单元的值,而会把已经分配过内存的p指针本身设置为0,造成内存泄漏。同理,对std::vector等数据类型,显而易见也是不应该使用memset来初始化的。

​ 除了对结构体内含有指针的结构体在使用memset的时候要注意,针对当结构体或类的本身或其基类中存在虚函数时,也需要谨慎使用memset

例如:

class BaseClass

{

public:

virtual void Test() {}

};

class MyClass : public BaseClass

{

public:

int data[3];

int buf[3];

};

MyClass myClassPar;

memset(&myClassPar, 0, sizeof(myClassPar));

BaseClass* pPar = &myClassPar;

//......

MyClass* my = dynamic_cast(pars);

​ 上述代码当运行到dynamic_cast时发生异常。原因其实就是在使用memset,使用memset目的是为了初始化数据结构MyClass里的data和buf,一般来说需要初始化的内存空间是sizeof(int) * 3 * 2 = 24字节,但是使用memset直接初始化MyClass类型的数据结构时,sizeof(myClassPar)却是28字节,之所以是28个字节是因为C++的多态,C++对有虚函数的对象会包含一个指向虚函数表(V-Table)的指针,当使用memset时,会把该虚函数表的指针也初始化为0,而dynamic_cast也使用RTTI(Run-Time Type Identification)技术,运行时会使用到V-Table,可此时由于与V-Table的链接已经被破坏,导致程序发生异常。

总结

在使用memset时,需要注意一下几方面:

注意memset的实质是对内存的单个字节进行操作;注意使用memset对象本身在系统中占有的字节大小,特别是进行非零赋值时;使用memset时尽量使用sizeof()关键字进行长度计算;针对含有指针类型的复杂数据类型,谨慎使用memset,避免内存泄漏;在使用提供的memcpy()、strcpy()、memmove()时也应注意与memset类似的问题。

实质是对内存的单个字节进行操作;

注意使用memset对象本身在系统中占有的字节大小,特别是进行非零赋值时;使用memset时尽量使用sizeof()关键字进行长度计算;针对含有指针类型的复杂数据类型,谨慎使用memset,避免内存泄漏;在使用提供的memcpy()、strcpy()、memmove()时也应注意与memset类似的问题。

相关推荐

365bet亚洲版登陆首页 2014年篮球世界杯

2014年篮球世界杯

365bet游戏网站 谢霆锋&张卫健《小鱼儿与花无缺》片尾曲 谢霆锋 - 黄种人

谢霆锋&张卫健《小鱼儿与花无缺》片尾曲 谢霆锋 - 黄种人

365bet亚洲版登陆首页 电脑打开特定的一个游戏(仅仅针对一个游戏),会不断卡死蓝屏,重新开机后再继续两三次这样的情况之后,就不会出现打开游戏蓝屏卡死的现象,但当关机的时候仍会出现蓝屏报错的现象。最近电脑无论什么游戏都会出现大幅度掉帧现象。蓝屏后只能强制重启不然无法继续操作。Win

电脑打开特定的一个游戏(仅仅针对一个游戏),会不断卡死蓝屏,重新开机后再继续两三次这样的情况之后,就不会出现打开游戏蓝屏卡死的现象,但当关机的时候仍会出现蓝屏报错的现象。最近电脑无论什么游戏都会出现大幅度掉帧现象。蓝屏后只能强制重启不然无法继续操作。Win