
我问你,哈布罗夫奇人!
今天,我想谈谈如何处理C或C ++中的内存泄漏。
Habré上已有两篇文章,分别是: 我们处理内存泄漏(C ++ CRT)和C ++中的内存泄漏:Visual Leak Detector 。 但是,我相信它们没有被充分公开,否则这些方法可能无法提供您所需的结果,因此,我想尽可能地找出所有可用的方法,以使您的生活更轻松。
Windows-开发
让我们从Windows开始,即为Visual Studio开发,因为大多数新手程序员都是专门为此IDE编写的。

为了了解发生了什么,我举一个真实的例子:
Main.cstruct Student create_student(); void ControlMenu(); int main() { ControlMenu(); return 0; } void ShowListMenu(int kX) { char listMenu[COUNT_LIST_MENU][55] = { {"Read students from file"}, {"Input student and push"}, {"Input student and push it back"}, {"Input student and push it after student"}, {"Delete last student"}, {"Write students to file"}, {"Find student"}, {"Sort students"}, {"Show list of students"}, {"Exit"} }; for (int i = 0; i < COUNT_LIST_MENU; i++) { if (i == kX) { printf("%s", listMenu[i]); printf(" <=\n"); } else printf("%s\n", listMenu[i]); } } void ControlMenu() { struct ListOfStudents* list = NULL; int kX = 0, key; int exit = FALSE; ShowListMenu(kX); do { key = _getch(); switch (key) { case 72:
还有Student.h
和Student.c
,其中声明了结构和功能。
有一个任务:演示没有内存泄漏。 首先想到的是CRT。 这里的一切都很简单。
在main所在的文件顶部,添加以下代码:
#define __CRTDBG_MAP_ALLOC #include <crtdbg.h> #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW
在return 0
之前,您需要注册以下代码: _CrtDumpMemoryLeaks();
。
结果,在Debug模式下,工作室将输出以下内容:
Detected memory leaks! Dumping objects -> {79} normal block at 0x00A04410, 376 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete.
太好了! 现在,您知道内存泄漏。 现在您需要消除这种情况,因此只需要找出我们忘记清除内存的位置即可。 这就产生了一个问题:实际上,此内存分配在哪里?

重复所有步骤后,我发现这里的内存丢失了:
if (kX == 0) { int sizeStudents = 0; struct Student* students = (struct Student*)malloc(1 * sizeof(struct Student)); char* path = (char*)malloc(255 * sizeof(char)); printf("Put the path to file with students: "); scanf("%s", path); int size = 0; students = read_students(path, &size); if (students == NULL) { printf("Can't open this file.\n"); } else { for (int i = 0; i < size; i++) { if (i == 0) { list = init(students[i]); } else { list = add_new_elem_to_start(list, students[i]); } } } free(students); printf("\nPress any key to continue..."); getchar(); getchar(); free(path); }
那怎么了 我要释放一切吗? 还是不行
在这里,我真的很想念Valgrind,它的通话记录...
结果,经过15分钟的步行,我发现了Valgrind- Visual Leak Detector的类似物。 这是一个第三方库,是对CRT的包装,它承诺显示跟踪信息! 这就是我所需要的。
要安装它,您需要转到存储库并在资产中找到vld-2.5.1-setup.exe
的确,上次更新是从Visual Studio 2015起的,但它可与Visual Studio 2019一起使用。安装是标准的,请按照说明进行操作。
要启用VLD,必须注册#include <vld.h>
。
该实用程序的优点是您不能在调试(F5)模式下运行,因为所有内容都显示在控制台中。 在开始时,将显示:
Visual Leak Detector read settings from: C:\Program Files (x86)\Visual Leak Detector\vld.ini Visual Leak Detector Version 2.5.1 installed.
这是内存泄漏时将产生的结果:
WARNING: Visual Leak Detector detected memory leaks! ---------- Block 1 at 0x01405FD0: 376 bytes ---------- Leak Hash: 0x555D2B67, Count: 1, Total 376 bytes Call Stack (TID 8908): ucrtbased.dll!malloc() test.exe!0x00F41946() test.exe!0x00F42E1D() test.exe!0x00F44723() test.exe!0x00F44577() test.exe!0x00F4440D() test.exe!0x00F447A8() KERNEL32.DLL!BaseThreadInitThunk() + 0x19 bytes ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xED bytes ntdll.dll!RtlGetAppContainerNamedObjectPath() + 0xBD bytes Data: CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD ........ ........ Visual Leak Detector detected 1 memory leak (412 bytes). Largest number used: 3115 bytes. Total allocations: 3563 bytes. Visual Leak Detector is now exiting.
在这里,我看到了痕迹! 那么,代码行在哪里? 函数名称在哪里?

好的,诺言没有兑现,但这不是我想要的结果。
我在Google中找到了一个选项:内存快照。 这很简单:在调试模式下,当您返回0时,需要转到诊断工具中的“内存使用情况”选项卡,然后单击“获取快照”。 如第一个屏幕截图所示,也许您将禁用此功能。 然后,您需要打开并重新启动调试。
拍照后,您会在堆下看到一个大小。 我认为这是程序期间分配的内存量。 点击这个尺寸。 我们将有一个窗口,其中包含存储在此堆中的对象。 要查看详细信息,需要选择一个对象,然后单击“ Foo对象表示实例”按钮。
是的 这是胜利! 完整的跟踪与呼叫位置! 这是最初需要的。
Linux-开发
现在,让我们看看在Linux上会发生什么。

在Linux上,存在valgrind实用程序。 要安装valgrind,您需要在控制台中注册sudo apt install valgrind
(对于Debian系列)。
我编写了一个填充动态数组的小程序,但同时未清除内存:
main.c #include <stdlib.h> #include <stdio.h> #define N 10 int main() { int * mas = (int *)malloc(N * sizeof(int)); for(int i = 0; i < N; i++) { *(mas+i) = i; printf("%d\t", *(mas+i)); } printf("\n"); return 0; }
使用CLang编译程序后,我们将获得一个.out文件,并将该文件丢给valgrind。
使用valgrind ./a.out
。 我认为valgrind是如何工作的,我认为在另一篇文章中对其进行描述是有意义的,现在,程序如何运行,valgrind将输出以下内容:
==2342== HEAP SUMMARY: ==2342== in use at exit: 40 bytes in 1 blocks ==2342== total heap usage: 2 allocs, 1 frees, 1,064 bytes allocated ==2342== ==2342== Searching for pointers to 1 not-freed blocks ==2342== Checked 68,984 bytes ==2342== ==2342== LEAK SUMMARY: ==2342== definitely lost: 40 bytes in 1 blocks ==2342== indirectly lost: 0 bytes in 0 blocks ==2342== possibly lost: 0 bytes in 0 blocks ==2342== still reachable: 0 bytes in 0 blocks ==2342== suppressed: 0 bytes in 0 blocks ==2342== Rerun with --leak-check=full to see details of leaked memory ==2342== ==2342== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ==2342== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
因此,到目前为止valgrind显示多少内存已丢失。 要查看分配内存的位置,您需要编写--leak-check=full
,然后valgrind除了上述内容外,还将输出以下内容:
==2348== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2348== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==2348== by 0x40053A: main (in /home/hunterlan/Habr/a.out)
当然,这里没有指定字符串,但是已经指定了一个函数,这是个好消息。
有valgrind的替代方法,例如strace或Dr.Memory,但我没有使用它们,它们主要用于valgrind无能为力的地方。
结论
我很高兴面对在Visual Studio中发现内存泄漏的问题,因为我学习了很多新工具,何时以及如何使用它们,并开始了解这些工具的工作原理。
感谢您的关注,给您编写好的代码!