为何MSVC编译器替换代码调用memset?如何禁用该优化?
循环清零代码被编译器优化为memset导致DLL链接错误的问题与解决
问题背景
环境与代码
使用Visual Studio 2022搭配v143平台工具集编写的DLL代码如下:
#include <Windows.h> unsigned char* buff; BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { int len = 1000; while (len--) *buff++ = 0; }
项目编译设置
- 编译器:禁用安全检查(
/GS-) - 链接器:
/NODEFAULTLIB、/ENTRY:"DllMain"
编译问题
编译器将手动编写的循环清零代码自动优化为调用标准库的memset函数,生成的汇编代码如下:
_DllMain@12 PROC ; COMDAT ; 7 : { push esi ; 8 : int len = 1000; mov esi, DWORD PTR ?buff@@3PAEA ; buff push 1000 ; 000003e8H push 0 push esi call _memset add esp, 12 ; 0000000cH add esi, 1000 ; 000003e8H mov DWORD PTR ?buff@@3PAEA, esi ; buff ; 9 : while (len--) ; 10 : *buff++ = 0; ; 11 : } xor eax, eax pop esi ret 12 ; 0000000cH _DllMain@12 ENDP
编译EXE文件时一切正常,但编译DLL时链接器报错:error LNK2001: unresolved external symbol _memset。
疑问
- 编译器为何会将手动循环替换为
memset调用? - 如何禁用该优化?
解决方案
通过添加volatile修饰的局部指针变量,可阻止编译器将循环优化为memset调用,修改后的代码如下:
#include <Windows.h> unsigned char* buff; BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { int len = 1000; volatile unsigned char* p = (unsigned char*)buff; while (len--) *p++ = 0; }
修改后生成的汇编代码不再调用memset,而是保留了手动循环的逻辑:
_DllMain@12 PROC ; 19 : int len = 1000; ; 20 : volatile unsigned char* p = (unsigned char*)buff; mov ecx, DWORD PTR ?buff@@3PAEA ; buff mov eax, 1000 ; 000003e8H $LL2@DllMain: ; 21 : while (len--) ; 22 : *p++ = 0; mov BYTE PTR [ecx], 0 inc ecx sub eax, 1 jne SHORT $LL2@DllMain ; 23 : } ret 12 ; 0000000cH _DllMain@12 ENDP
内容的提问来源于stack exchange,提问作者user774076
相关产品推荐
相关产品推荐

