使用ZeroMemory、memset对结构体、数组的初始化

本文深入探讨了memset和ZeroMemory函数的使用方法及其区别,包括它们在初始化结构体和数组时的行为,特别是在处理虚函数和STL模板时的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用memset初始化:

memset函数原型:void * memset(void *ptr,int value,size_t num);  作用:用于为地址ptr开始的num个字节赋值value

typedef struct s1
{
	SOCKET		m_Socket;
	SOCKADDR_IN m_ClientAddr;

	//初始化
	s1()
	{
		m_Socket = INVALID_SOCKET;
		//SOCKADDR_IN本身也是个结构体可以使用memset方法初始化
		memset(&m_ClientAddr, 0, sizeof(m_ClientAddr));
	}
};

struct Stu
{
	int nNum;
	bool bSex;
	char szName[20];
	char szEmail[100];

	//这样初始化也可以
	Stu()
	{
		memset(this, 0, sizeof(Stu));

		//或者下面的格式
		//memset(&nNum, 0, sizeof(Stu));
	}
};

使用ZeroMemory的初始化

ZeroMemory只有将数据置零的功能,在windows平台下,数组或纯结构使用ZeroMemory是安全的

typedef struct _PER_IO_CONTEXT
{
	OVERLAPPED		m_Overlapped;	//每一个重叠网络操作的重叠结构(针对每一个Socket的每一个操作,都要有一个)
	SOCKET			m_sockAccept;	//这个网络操作所使用的Socket
	WSABUF			m_wsaBuf;		//WSA类型的缓冲区,用于给重叠操作传参数
	char			m_szBuffer[MAX_BUFFER_LEN]; //WSABUF里具体存字符的缓冲区
	OPERATION_TYPE	m_OpType;		//标识网络操作的类型

	//初始化
	_PER_IO_CONTEXT()
	{
		ZeroMemory(&m_Overlapped, sizeof(m_Overlapped));
		ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
		m_sockAccept = INVALID_SOCKET;
		m_wsaBuf.buf = m_szBuffer;
		m_wsaBuf.len = MAX_BUFFER_LEN;
		m_OpType = NULL_POSTED;
	}

	//释放掉Socket
	~_PER_IO_CONTEXT()
	{
		if (m_sockAccept != INVALID_SOCKET)
		{
			closesocket(m_sockAccept);
			m_sockAccept = INVALID_SOCKET;
		}
	}

	//重置缓冲区内容
	void ResetBuffer()
	{
		ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
	}
}PER_IO_CONTEXT,*PPER_IO_CONTEXT;

注意事项:

(1)  不能认为memset可以赋值为任意的值,因为memset函数在赋值时是逐个字节赋值的,如果是一个int类型的数组,比如int a[10];其中每个元素占有4字节的大小,如果执行memset(a,1,sizeof(a));则此时并不是将数组里面所有值都赋值为1,而是将每个元素的4个字节分别赋值为1,则其二进制大小为0000 0001 0000 0001 0000 0001 0000 0001,也就是将4组值为1的二进制连接起来,所以此时数组中每个元素大小为16843009,与其预期不符,但是可以使用0或者-1为int类型数组赋值,因为0对应的二进制为0000,-1对应的二进制位1111,所以在逐字节赋值的时候不会产生赋值错误。

(2)  可以对char类型数组进行不同值的赋值,因为char类型正好只占用1字节,赋的值只要为char类型则不会出现(1)中的错误

(3)  ZeroMemory和memset在清零时回将结构体中所有字节置为0,如果结构体中含有虚函数或结构体成员中有虚函数,则会将虚函数指针置为0,如果后续程序通过指针调用虚函数,则这个空指针会造成崩溃,另外如果一个类的结构中包含STL模板(Vector、List、Map等),那么使用ZeroMemory对这个类的对象中进行清零操作也会引起一系列的崩溃问题(指针指向内存错误、迭代器越界访问等),所以建议:类只使用构造函数进行初始化,不要调用ZeroMemory进行清零操作

#include <iostream>
#include <string>
using namespace std;

struct s1
{
	int i;
	virtual void  fun(int a)
	{
		i = a;
	}
};

int main()
{
	s1 s;
	s.i = 10;
	s.fun(100);
	cout << s.i << endl;  //100
	memset(&s, 0, sizeof(s));
	s.fun(88);
	cout << s.i << endl;  //88
	(&s)->fun(99);  //此处引发了异常 __vfptr = 0x00000000,崩溃
	cout << s.i << endl; //并不能打印出数值
}

如上程序,在置0后,只有使用指针来调用该虚函数时会产生崩溃,通过对象调用则不会产生崩溃,并且如果函数只是普通函数而非虚函数,则置0后不会发生错误,因为普通函数没有虚函数指针,虚函数指针才会占用空间,所以在置0时被置为空指针。

ZeroMemory和memset的区别:

(1)  ZeroMemory是微软的SDK提供的,memset属于C Run-time Library提供的,因此ZeroMemory只能用于Windows系统,而memset还可用于其他系统
(2)  ZeroMemory是一个宏,只用于把一段内存的内容置零,内部其实是用memset实现的,而memset除了对内存进行清零操作,还可以将内存置成别的字符
(3)  如果程序是Win32程序而且不想连接C运行时库,那就用ZeroMemory,如果需要跨平台,那就用memset

ZeroMemory和“={0}”的区别:

(1)  ZeroMemory会将结构中所有字节置0,而“={0}”只会讲成员置0,其中填充字节不变
(2)  一个struct有构造函数或虚函数时,ZeroMemory可以编译通过,而“={0}”会产生编译错误,其中,“={0}”的编译错误起到了一定的保护作用,因为对一个有虚函数的对象使用ZeroMemory时,会将其虚函数的指针置0,这是非常危险的(调用虚函数时,空指针很可能引起程序崩溃)

部分借鉴自:https://www.cnblogs.com/lidabo/archive/2013/01/07/2848701.html

<think>好的,我需要回答用户关于如何使用memset初始化内存或变量的问题。首先,我要回忆一下memset的基本用法和相关知识点。根据用户提供的引用资料,memset是C/C++中的一个函数,用于将一段内存块填充为指定的值。它的原型是void *memset(void *s, int c, size_t n),其中s是起始地址,c是要填充的值,n是填充的字节数。 接下来,我需要整理正确的使用步骤和示例。用户提到的引用中,有提到memset常用于结构体数组初始化,特别是清零操作。例如,引用[2]指出memset是对较大的结构体数组进行清零的最快方法。同时,引用[5]提到对于int数组,建议使用fill或fill_n而不是memset,这可能是因为memset按字节操作,而int类型可能涉及多字节的问题。 首先,我应该说明memset的基本用法,包括函数原型和参数解释。然后给出一些常见的使用场景,比如初始化结构体数组清零等。同时,需要注意潜在的问题,例如字节填充时的值是否正确,特别是当初始化非字符类型时可能出现的错误。例如,用memset将int数组初始化为0是正确的,因为每个字节都被设为0,但如果设为1,每个int元素会是0x01010101,这可能不是预期的结果。 然后,我需要引用用户提供的资料来支持这些观点。例如,引用[1]提到memset是跨平台的,而ZeroMemory是Windows特有的宏。引用[3]展示了结构体初始化方法,但用memset可以快速清零结构体内存。此外,引用[5]建议对于int数组使用fill而不是memset,这可能是因为memset在处理非字符类型时容易出错,比如每个字节都被设置为相同的值,导致整个int的值不符合预期。 接下来,我应该给出具体的代码示例,比如初始化结构体数组清零,以及可能的错误用法。同时,需要指出注意事项,比如内存对齐问题(引用[1]提到microLib没有字节对齐,导致性能问题)、使用已释放内存的风险(引用[4]中的例子)等。 最后,生成相关问题,帮助用户进一步了解相关知识。例如,memset和fill的区别,内存对齐的影响,或者结构体初始化的其他方法。 在整理回答时,需要确保结构清晰,分点说明,并正确引用资料。同时,要遵循用户给出的格式要求,如行内公式使用$...$,独立公式用$$...$$,但在这个问题中可能不需要数学公式。另外,使用中文回答,确保LaTeX语法正确,虽然这个问题可能不需要太多LaTeX,但如果有代码块,需要用正确的格式。 现在,将这些思路整理成一个结构化的回答,包含基本用法、示例、注意事项和相关问题。</think>### 如何使用 memset 初始化内存或变量 #### 1. memset 基本用法 `memset` 是 C/C++ 中用于内存初始化的函数,其原型为: ```c void *memset(void *s, int c, size_t n); ``` - **参数说明**: - `s`:目标内存的起始地址 - `c`:填充值(以 `int` 形式传递,但实际按字节处理) - `n`:填充的字节数 - **返回值**:返回 `s` 的地址。 #### 2. 常见使用场景 ##### (1) 结构体清零 对结构体进行快速初始化时,`memset` 是最高效的方式[^2]: ```c struct Person { char name[20]; int age; float salary; }; struct Person p1; memset(&p1, 0, sizeof(p1)); // 所有成员清零 ``` ##### (2) 数组初始化 - **字符数组填充**: ```c char str[] = "hello"; memset(str, '-', 3); // 前3字节变为'-',结果为:---lo ``` - **整型数组清零**(仅限全0初始化): ```c int arr[10]; memset(arr, 0, sizeof(arr)); // 正确:所有元素为0 ``` ⚠️ 若需非零初始化(如全1),直接使用 `memset` 会导致错误(见下文注意事项)。 #### 3. 注意事项 1. **非字符类型的陷阱** `memset` 按字节填充,若对 `int` 数组填充非0值会导致错误: ```c int arr[10]; memset(arr, 1, sizeof(arr)); // 错误!每个int变为0x01010101(非1) ``` 此时应改用 `fill` 或 `fill_n`(C++推荐)[^5]: ```cpp int arr[10]; fill(arr, arr+10, 1); // 所有元素正确初始化为1 ``` 2. **内存对齐问题** 某些嵌入式平台(如 microLib)因未对齐内存,使用 `memset` 可能导致性能下降[^1]。 3. **跨平台兼容性** `memset` 是标准库函数,可跨平台使用;而 `ZeroMemory` 是 Windows 宏(底层仍调用 `memset`)[^1]。 4. **内存越界风险** 需确保填充范围不超过目标内存大小,否则可能引发未定义行为(如内存泄漏或崩溃)[^4]。 #### 4. 错误用法示例 ```c int *array = new int[100]; delete[] array; memset(array, 0, 100*sizeof(int)); // 错误!已释放的内存不可操作[^4] ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值