LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

Visual Studio下的内存安全检测:CRT 内存泄漏 & AddressSanitizer

zhenglin
2025年10月21日 15:7 本文热度 79

Visual Studio下的内存安全检测:CRT 内存泄漏 & AddressSanitizer

前言

笔者之前有一篇博客专门介绍了GNU工具链下的gcc如何启动AddressSanitizer来检查内存安全检查


 但是我发现,很多朋友包括我自己之后的工作是在Windows上做的,为此就有必要单独拉出来谈一谈Visual Studio(或者是使用MSVC编译器的命令行)是如何检查我们的内存安全的。这里我们列出来三个渠道。笔者比较建议第二个来检查内存泄露,第三个检查检测越界和 Use-after-free等经典内存误用问题。


使用 CRT Debug Heap 检测内存泄漏;

使用 Diagnostic Tools → Memory Usage 分析堆快照和分配调用栈;

了解 AddressSanitizer 检测越界和 Use-after-free




内存泄漏检测(CRT Debug Heap)

Microsoft C Runtime自己就有Debug插桩机制来检查我们的内存安全。如果您不太相信,您可以这样做:

#define _CRTDBG_MAP_ALLOC // 定义在待检查文件的开头


#include <crtdbg.h> // 这个头文件要被包含进来,从而使用代码层次上的插桩


#include <cstdlib>

#include <iostream>

#include <cstring>


// CRT Debug要在运行期启动,这不奇怪,都是C RunTime提供的机制了,自然运行期执行代码对不对?

void enable_memory_debug() {

    int dbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

    dbgFlags |= _CRTDBG_LEAK_CHECK_DF;

    dbgFlags |= _CRTDBG_ALLOC_MEM_DF;

    _CrtSetDbgFlag(dbgFlags);


    // 强制 CRT 报告输出到 stdout, 下面这几步至少在VS2026是必须要做的

    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);

    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);

_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);

_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);

_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);

_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);

}


int main() {

    enable_memory_debug();


    char* p = new char[10];

    strcpy_s(p, 10, "Hello!");


    std::cout << "Expected to trigger the overflow check!";   

}

很好,到这里了,程序一结束,我们的程序尾巴就会打印内从,如下所示


Detected memory leaks!

Dumping objects ->

{195} normal block at 0x00000246134FADC0, 10 bytes long.

 Data: <Hello!    > 48 65 6C 6C 6F 21 00 CD CD CD

Object dump complete.

这就对了,但是您有没有发现,他只是说明了咱们的内存泄露是如何的,但是完全没说在哪里泄露了。这是没有意义的。所以,我们需要费劲的写下这段代码


#ifdef _DEBUG

#define DEBUG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )

#define new DEBUG_NEW

#endif


这段代码的意思是——将new重新定义为——插桩了文件所在地址和行数的new,然后定义回去。

main.cpp(36) : {195} normal block at 0x00000246134FADC0, 10 bytes long.

现在我们的信息就多出来了!


值得一提的是,犯不着等到程序结束让程序自动触发打印检查,只需要我们调用_CrtDumpMemoryLeaks();这个函数就会打印我们的泄露情况。



在特定分配断点

但是好像还是不够方便。有时候重定向new看起来怪怪的,我们改变了程序的分配内存行为,有没有比较低侵入的办法呢?有。


您有注意到{195} normal block at 0x00000246134FADC0, 10 bytes long.的195没?注意到的话,这就是我们内存分配块的分配序号。

这个时候,我们可以在代码的开头(需要注意的是,是在enable_debug()操作之后做,以及最好是任何new之前做。)写下F5 调试,程序会在该次分配断下,这个时候,我们就检查调用堆栈,看看到底是哪一行的分配我们忘记释放了。

_CrtSetBreakAlloc(195);




Diagnostic Tools:堆快照分析

您会发现(笔者实际上做的时候就有感觉了),方式一比较原始,而且我们要做的处理非常繁杂,甚至还需要专门准备开启打印的内容,其实适合的是自己代码就有意检查的模块部分,但是我相信大部分情况下大家都是在救急。

期望赶快找到是哪里程序的内存分配行为错误了。这个时候Diagnostic Tools就显得十分的重要的了。

请注意,建议先打开断点,随便在main的起头把断点上了,然后Debug → Windows → Show Diagnostic Tools。

注意,内存诊断要开启,笔者第一次做没有开启就发现不允许拍内存快照,之后,我们需要做的是:

  • ​运行程序到想分析的点,点击 Take snapshot(Snapshot #1);

  • 继续运行,到你认为程序一个功能职责结束的地方拍 Snapshot #2;

  • 查看 Diff:增长对象表示潜在泄漏。


这是笔者的一个截图,您能看到右下角的位置上,本来我们预期是内存全部释放的地方上却上升了0.06KB(约10B,恰好就是我们泄露的点)


当然,点击对象类型的项目,你就会自动跳转到到底是谁干的分配了,这里不再赘述。

如果我们添加了delete[] p,那就是用完后内存就被归还了。再拍两次快照,对比差异接近 0,泄漏被修复。




3. AddressSanitizer(ASan)

如果对AddressSanitizer还不知道是啥的,笔者建议您去我开头提到的博客看看:

 AddressSanitizer 实践:几个常见的错误


Visual Studio下开启并不困难,只需要勾选:

  • ​Project → Properties → C/C++ → General → Enable Address Sanitizer = Yes (/fsanitize=address)(Tips:中文的话是启用地址擦除系统,找了好久)

  • Debug x64 模式,Clean rebuild 项目。


3.1 越界示例


char* p = new char[8];

for (int i = 0; i < 12; ++i) p[i] = 'A'; // OOB

delete[] p;


3.2 Use-after-free 示例


char* p = new char[16];

strcpy_s(p, 16, "hello asan");

delete[] p;

std::cout << p[0] << "\n"; // UAF


运行后,ASan 会输出:

AddressSanitizer: heap-buffer-overflow

AddressSanitizer: heap-use-after-free

同时显示访问地址、偏移、分配栈和释放栈。



4. 小结与建议

  • ​CRT Debug Heap:快速定位泄漏,配合 DEBUG_NEW 和 _CrtSetBreakAlloc,适合单次或短小程序分析。

  • ​Diagnostic Tools:适合交互式堆快照对比,可在调试时精确定位泄漏对象和调用栈。

  • AddressSanitizer:针对越界和 Use-after-free,非常精确,适合调试复杂堆访问错误。

组合使用这三种工具,你可以在 Windows 下 高效发现和修复 C++ 内存安全问题



参考文章:原文链接


该文章在 2025/10/21 15:08:21 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved