memset 、ZeroMemory和 “={0}” 三者区别

本文详细解释了ZeroMemory和memset两个函数的区别,并对比了它们与初始化语法“={0}

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

memset是以字节为单位,初始化内存块。
当初始化一个字节单位的数组时,可以用memset把每个数组单元初始化成任何你想要的值,比如
char data[10];  
memset(data, 1, sizeof(data));    // right  
memset(data, 0, sizeof(data));    // right 

而在初始化其他基础类型时,则需要注意,比如,
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

以上内容引用自:http://blog.csdn.net/my_business/article/details/40537653#

ZeroMemorymemset 和 “={0}” 三者区别:

首先是ZeroMemorymemset的区别:

1、ZeroMemory是微软的SDK提供的,memset属于C Run-time Library提供的。
因此ZeroMemory只能用于Windows系统,而memset还可用于其他系统。


2、ZeroMemory是一个宏,只是用于把一段内存的内容置零,内部其实是用 
memset实现的,而memset除了对内存进行清零操作,还可以将内存置成别的字符。


3、如果程序是Win32程序而且不想连接c运行时库,那就用ZeroMemory,如果需要跨平台,那就用memset

所以如果ZeroMemorymemset用于清零操作,其本质是一样的。

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

1、ZeroMemory会将结构中所有字节置0,而“={0}”只会将成员置0,其中填充字节不变。

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


/*----------------------------------------------------------------------- 第13章 Hook技术 《加密与解密(第四版)》 (c) 看雪学院 www.kanxue.com 2000-2018 -----------------------------------------------------------------------*/ // VEHHook.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <windows.h> BOOL SetBreakPoint(PVOID pFuncAddr); BOOL ClearBreakPoint(PVOID pFuncAddr); BOOL InstallVEHHook(PVECTORED_EXCEPTION_HANDLER Handler); VOID UnInstallVEHHook(); typedef int (WINAPI *PFN_MessageBox)( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ); int WINAPI My_MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ); LONG WINAPI VectoredHandler1(struct _EXCEPTION_POINTERS *ExceptionInfo); LONG WINAPI VectoredHandler2(struct _EXCEPTION_POINTERS *ExceptionInfo); LONG WINAPI VectoredHandler3(struct _EXCEPTION_POINTERS *ExceptionInfo); VOID ShowMsgBox(LPCTSTR lpMsg); ULONG_PTR InitTrampolineFun(); PFN_MessageBox g_OriginalMessageBoxA; PVOID g_AddrofMessageBoxA = 0 ; PVOID g_hVector; BYTE g_OldCode[16]={0}; int main(int argc, char* argv[]) { HMODULE hUser32 = LoadLibrary("user32.dll"); g_AddrofMessageBoxA = (PVOID)GetProcAddress(hUser32,"MessageBoxA"); printf("Address of MessageBoxA = 0x%p\n",g_AddrofMessageBoxA); g_OriginalMessageBoxA = (PFN_MessageBox)InitTrampolineFun(); //跳过开头的Hook printf("Addr of VectoredHandler1 = 0x%p\n",VectoredHandler1); printf("Addr of VectoredHandler2 = 0x%p\n",VectoredHandler2); printf("Addr of VectoredHandler3 = 0x%p\n",VectoredHandler3); //选择安装一个进行测试 InstallVEHHook(VectoredHandler1); //设置断点 SetBreakPoint(g_AddrofMessageBoxA); //call ShowMsgBox("VEH Hook Test."); printf("All Finished!\n"); ClearBreakPoint(g_AddrofMessageBoxA); UnInstallVEHHook(); ShowMsgBox("Hook Cleared"); return 0; } VOID ShowMsgBox(LPCTSTR lpMsg) { MessageBoxA(NULL,lpMsg,"Test",MB_OK); } ULONG_PTR InitTrampolineFun() { ULONG_PTR uResult = 0 ; PBYTE pFun = NULL; #ifdef _WIN64 //x64需要申请shellcode /* USER32!MessageBoxA: 00000000`779412b8 4883ec38 sub rsp,38h 00000000`779412bc 4533db xor r11d,r11d 00000000`779412bf 44391d760e0200 cmp dword ptr [USER32!gapfnScSendMessage+0x927c (00000000`7796213c)],r11d */ pFun = (PBYTE)VirtualAlloc(NULL,128,MEM_COMMIT,PAGE_EXECUTE_READWRITE); uResult = (ULONG_PTR)pFun; memset(pFun,0,128); memcpy(pFun,(PVOID)g_AddrofMessageBoxA,4); //拷贝第一条指令,4字节,推荐使用反汇编引擎来实际计算 pFun += 4 ; //下一条指令构造为jmp [xxxxxx] pFun[0] = 0xFF; pFun[1] = 0x25; *(ULONG_PTR*)(pFun + 6) = (ULONG_PTR)g_AddrofMessageBoxA + 4 ; //跳回到原函数加4的地方 #else //x86,第一条指令是mov edi,edi,直接跳过即可 uResult = (ULONG_PTR)g_AddrofMessageBoxA + 2; #endif return uResult; } //处理方式,修改参数并返回原函数继续执行 LONG WINAPI VectoredHandler1( struct _EXCEPTION_POINTERS *ExceptionInfo ) { char *szNewMsg = "[VectoredHandler1] Hacked by pediy.com"; LONG lResult = EXCEPTION_CONTINUE_SEARCH ; PEXCEPTION_RECORD pExceptionRecord; PCONTEXT pContextRecord; int ret = 0 ; pExceptionRecord = ExceptionInfo->ExceptionRecord ; pContextRecord = ExceptionInfo->ContextRecord ; ULONG_PTR* uESP = 0 ; printf("Exception Address = %p\n",pExceptionRecord->ExceptionAddress); if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT && pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA) { #ifdef _WIN64 //x64上前四个参数依次为RCX,RDX,R8,R9 //修改第二个参数,即LpMsg printf("lpText = 0x%p %s\n",pContextRecord->Rdx,(char*)pContextRecord->Rdx); pContextRecord->Rdx = (ULONG_PTR)szNewMsg; pContextRecord->Rip = (ULONG_PTR)g_OriginalMessageBoxA ; //跳到Trampoline继续执行 #else /* 0012FF70 0040105A /CALL 到 MessageBoxA 来自 VEHHook.00401054 0012FF74 00000000 |hOwner = NULL 0012FF78 00407030 |Text = "VEH Hook" 0012FF7C 0040703C |Title = "Test" 0012FF80 00000000 \Style = MB_OK|MB_APPLMODAL 0012FF84 00401225 返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000 */ printf("ESP = 0x%p\n",pContextRecord->Esp) ; uESP = (ULONG_PTR*)pContextRecord->Esp ; //取中断时的ESP uESP[2] = (ULONG_PTR)szNewMsg; //修改栈中的参数 pContextRecord->Eip = (ULONG_PTR)g_OriginalMessageBoxA ; //跳过函数开头 #endif lResult = EXCEPTION_CONTINUE_EXECUTION ; } return lResult; } //处理方式:直接调用原函数并替原函数返回 LONG WINAPI VectoredHandler2( struct _EXCEPTION_POINTERS *ExceptionInfo ) { char *szNewMsg = "[VectoredHandler2] Hacked by pediy.com"; LONG lResult = EXCEPTION_CONTINUE_SEARCH ; PEXCEPTION_RECORD pExceptionRecord; PCONTEXT pContextRecord; int ret = 0 ; pExceptionRecord = ExceptionInfo->ExceptionRecord ; pContextRecord = ExceptionInfo->ContextRecord ; ULONG_PTR* uESP = 0 ; if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT && pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA) { #ifdef _WIN64 //x64上前四个参数依次为RCX,RDX,R8,R9 printf("RSP = 0x%p\n",pContextRecord->Rsp) ; uESP = (ULONG_PTR*)pContextRecord->Rsp ; printf("Return Address = 0x%p\n",uESP[0]); ret = g_OriginalMessageBoxA((HWND)pContextRecord->Rcx,szNewMsg,(LPCTSTR)pContextRecord->R8,(int)pContextRecord->R9); printf("ret = %d\n",ret); //修正RSP pContextRecord->Rsp += sizeof(ULONG_PTR);//参数在寄存器中,栈中无参数,仅需跳过返回地址 //直接返回到调用者处 pContextRecord->Rip = uESP[0] ;//设置EIP为返回地址 #else /* 0012FF70 0040105A /CALL 到 MessageBoxA 来自 VEHHook.00401054 0012FF74 00000000 |hOwner = NULL 0012FF78 00407030 |Text = "VEH Hook" 0012FF7C 0040703C |Title = "Test" 0012FF80 00000000 \Style = MB_OK|MB_APPLMODAL 0012FF84 00401225 返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000 */ printf("ESP = 0x%p\n",pContextRecord->Esp) ; uESP = (ULONG_PTR*)pContextRecord->Esp ; ret = g_OriginalMessageBoxA((HWND)uESP[1],szNewMsg,(LPCTSTR)uESP[3],(int)uESP[4]); printf("ret = %d\n",ret); //直接返回到调用者处 pContextRecord->Eip = uESP[0] ;//设置EIP为返回地址 pContextRecord->Esp += (4 + 1)*sizeof(ULONG_PTR); //4为参数个数,1为返回地址 #endif lResult = EXCEPTION_CONTINUE_EXECUTION ; } return lResult; } //处理方式:直接返回,相当于过滤掉 LONG WINAPI VectoredHandler3( struct _EXCEPTION_POINTERS *ExceptionInfo ) { LONG lResult = EXCEPTION_CONTINUE_SEARCH ; PEXCEPTION_RECORD pExceptionRecord = ExceptionInfo->ExceptionRecord ; PCONTEXT pContextRecord = ExceptionInfo->ContextRecord ; ULONG_PTR* uESP = 0 ; if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT && pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA) { /* 0012FF70 0040105A /CALL 到 MessageBoxA 来自 VEHHook.00401054 0012FF74 00000000 |hOwner = NULL 0012FF78 00407030 |Text = "VEH Hook" 0012FF7C 0040703C |Title = "Test" 0012FF80 00000000 \Style = MB_OK|MB_APPLMODAL 0012FF84 00401225 返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000 */ //直接返回到调用者处 #ifdef _WIN64 printf("RSP = 0x%p\n",pContextRecord->Rsp) ; uESP = (ULONG_PTR*)pContextRecord->Rsp ; pContextRecord->Rip = uESP[0] ;//设置EIP为返回地址 pContextRecord->Rsp += sizeof(ULONG_PTR); //将压入栈内的参数返回地址清理掉,4为参数个数,1为返回地址 #else printf("ESP = 0x%X\n",pContextRecord->Esp) ; uESP = (ULONG_PTR*)pContextRecord->Esp ; pContextRecord->Eip = uESP[0] ;//设置EIP为返回地址 pContextRecord->Esp += (4 + 1)*sizeof(ULONG_PTR); //将压入栈内的参数返回地址清理掉,4为参数个数,1为返回地址 #endif lResult = EXCEPTION_CONTINUE_EXECUTION ; } return lResult; } BOOL InstallVEHHook(PVECTORED_EXCEPTION_HANDLER Handler) { printf("Current Handler Address = 0x%p\n",Handler); g_hVector = AddVectoredExceptionHandler(1,Handler); return g_hVector != NULL ; } VOID UnInstallVEHHook() { RemoveVectoredExceptionHandler(g_hVector); } /* 0:000> u user32!messageboxA USER32!MessageBoxA: 77d507ea 8bff mov edi,edi 77d507ec 55 push ebp 77d507ed 8bec mov ebp,esp */ BOOL SetBreakPoint(PVOID pFuncAddr) { DWORD dwCnt = 0 ; BYTE *pTarget = (BYTE*)pFuncAddr; g_OldCode[0] = *pTarget; printf("Original Fun Head Code = 0x%02X\n",g_OldCode[0]); //修改内存页的属性 DWORD dwOLD; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(pTarget,&mbi,sizeof(mbi)); VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD); //写入int3 *pTarget = 0xCC ; //恢复内存页的属性 VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0); return TRUE; } BOOL ClearBreakPoint(PVOID pFuncAddr) { BYTE *pTarget = (BYTE*)pFuncAddr; //修改内存页的属性 DWORD dwOLD; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(pTarget,&mbi,sizeof(mbi)); VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD); *pTarget = g_OldCode[0] ; //恢复内存页的属性 VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0); return TRUE; } int WINAPI My_MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ) { char newMsg[400]; char newCation[]="标题被我改了!"; int result; if (lpText) { ZeroMemory(newMsg,400); lstrcpy(newMsg,lpText); lstrcat(newMsg,"\n\tMessage Box hacked by pediy.com"); } printf("有人调用MessageBox...\n"); result = g_OriginalMessageBoxA(hWnd,newMsg,newCation,uType); return result; }分析是什么代码?有什么作用?如何执行
最新发布
07-25
<think>嗯,用户想了解ZeroMemorymemset的用途区别。这个问题很基础但很重要,涉及C/C++内存操作的核心知识。 用户应该是个开发者,可能在Windows平台编程时遇到了内存初始化问题。从引用材料看,用户已经查阅了一些资料,但需要系统性的对比。需要特别注意用户提到的引用[3][4],里面提到三种清零方式的区别,这可能是用户真正关心的深层问题。 先整理核心区别点: - ZeroMemory是Windows SDK宏,本质调用memset - memset是标准C库函数,更跨平台 - 两者在清零操作上等价,但memset功能更强大 用户可能没意识到的是陷阱问题:比如用ZeroMemory清零带虚函数的类会破坏虚表指针。这个必须重点说明,引用[3]也提到容易出严重错误。 还要注意使用场景的适用性: - 纯C结构体用两者都可以 - C++类对象推荐用构造函数初始化 - POD类型数组初始化用memset更灵活 需要强调安全用法,比如引用[4]提到的={0}初始化方式虽然简洁,但有局限性。这个对比表格应该能帮用户快速掌握核心差异。 最后考虑用户可能的后续问题: - 性能差异实际可以忽略,但有人会纠结 - 在Linux下替代方案 - 更安全的现代C++替代方案 - 调试时内存填充模式 回复时要注意把技术细节说透,特别是虚函数那个坑,很多老手都栽过。用表格对比很清晰,但需要补充文字说明注意事项。代码示例选最常见的结构体清零场景就够了,太复杂反而不利于理解。</think>以下是关于`ZeroMemory`与`memset`的详细对比分析,结合其用途、区别及注意事项: --- ### **核心区别总结** | **特性** | **ZeroMemory** | **memset** | |------------------|-----------------------------------------|-----------------------------------------| | **本质** | Windows SDK 宏(通过 `memset` 实现) | C/C++ 标准库函数 | | **跨平台性** | ❌ 仅限 Windows 平台 | ✅ 所有支持 C/C++ 的平台 | | **功能范围** | 仅能清零内存(固定填充 `0`) | 可填充任意字节值(如 `0`, `'A'`, `0xFF`) | | **安全性** | 对带虚函数的类有风险(破坏虚表指针) | 同左,需谨慎用于非POD类型 | | **语法示例** | `ZeroMemory(&obj, sizeof(obj));` | `memset(&obj, 0, sizeof(obj));` | --- ### **详细解析** #### 1. **实现原理** - **ZeroMemory** 本质是微软定义的宏,底层调用 `memset`: ```c #define RtlZeroMemory(Destination, Length) memset((Destination), 0, (Length)) #define ZeroMemory RtlZeroMemory ``` 仅用于内存清零(固定填充 `0`)[^1][^2]。 - **memset** 标准库函数,可填充**任意指定值**: ```c void* memset(void* dest, int ch, size_t count); ``` 例如填充 `'-'`: ```c char str[] = "Hello"; memset(str, '-', 3); // 结果: "---lo" ``` #### 2. **关键区别** - **功能灵活性** `memset` 可通过参数自由设定填充值(如初始化非零模式),而 `ZeroMemory` 只能清零。 - **平台依赖性** `ZeroMemory` 是 Windows API 的一部分,若代码需跨平台(如 Linux),应使用 `memset`。 #### 3. **风险与陷阱** - **对含虚函数的类清零会引发未定义行为** 两者均直接操作内存,若对象包含虚函数指针(`vptr`),清零后会破坏虚表结构,导致程序崩溃: ```cpp class MyClass { public: virtual void foo() {} // 含虚函数 → 有虚表指针 }; MyClass obj; ZeroMemory(&obj, sizeof(obj)); // 危险!虚表指针被清零 obj.foo(); // 运行时崩溃! ``` - **替代方案** 对非POD类型(如带构造函数的类),优先使用构造函数或 `={0}` 初始化: ```cpp // 安全做法 MyClass obj = {}; // C++11 统一初始化 MyClass obj{}; // 推荐初始化方式 ``` #### 4. **适用场景** - **推荐使用 `ZeroMemory` / `memset` 的场景** - 初始化 **POD类型**(基本类型、C风格结构体): ```cpp struct Point { int x, y; }; // POD类型 Point p; ZeroMemory(&p, sizeof(p)); // 安全清零 ``` - 清空缓冲区(如网络接收缓存区)。 - **禁止使用的情况** - 带虚函数的类、STL容器(`std::vector` 等)、智能指针等非POD类型。 --- ### **代码示例对比** ```c #include <Windows.h> #include <cstring> struct Data { // POD类型 int id; float value; }; int main() { Data d1, d2; // 等效清零操作 ZeroMemory(&d1, sizeof(d1)); // Windows API memset(&d2, 0, sizeof(d2)); // 标准C++ // 错误示例:对非POD类型清零 std::string s; memset(&s, 0, sizeof(s)); // 破坏内部指针!绝对禁止! } ``` --- ### **总结建议** - ✅ **需要跨平台 → 用 `memset`** - ✅ **仅 Windows 且需清零 → `ZeroMemory` 可读性更佳** - ❌ **非POD类型(类、结构体含虚函数)→ 使用构造函数初始化** - ⚠️ **重要提示**:优先选择类型安全的初始化方式(如 `={0}`、`{}`),避免手动内存操作。 --- ### 相关问题 1. **如何安全初始化包含虚函数的C++对象?** 2. **在Linux开发中替代`ZeroMemory`的方案是什么?** 3. **`memset`填充非零值时有哪些典型应用场景?** 4. **为什么对STL容器使用`memset`会导致程序崩溃?** 5. **C++11后的现代化初始化方式如何避免内存操作风险?** [^1]: ZeroMemory 通过宏调用 `memset` 实现,功能为固定清零。 [^2]: `memset` 是标准C库函数,可灵活填充任意字节值。 [^3]: 对含虚函数的类使用 `ZeroMemory/memset` 会破坏虚表指针,引发未定义行为。 [^4]: 非POD类型推荐使用构造函数或 `={0}` 初始化,避免手动内存操作。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值