库函数 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类似的问题。