查找 C++ 中的内存泄漏
本文将使用 C++ 编程语言解释内存泄漏、其原因、如何识别它们以及如何防止它们。
C++ 中的内存泄漏
如果程序员先前分配给一个目的的部分内存被用于另一个目的,则称内存“泄漏”。 当发生这样的事情时,程序员没有正确地释放资源。
此时,程序不需要使用这样的RAM。 因此,没有理由维持该座位的预订。
在C++中,当程序员使用new关键字分配内存,但无法使用 delete( )
函数或 delete[]
运算符释放内存时,就会发生内存泄漏; 这会导致记忆丧失。 内存泄漏大多数时候是由不正确的删除操作引起的。
delete[]
运算符可以释放数组中的数据。
如果我们在更换汽车机油的同时忽略了更换密封件或拧紧螺丝,那么我们在驾驶时很可能会遇到严重问题。
我们汽车的发动机最终会失灵,因为所有的油最终都会从汽车中流出; C++ 编程将导致完全相同的结果。
当程序员动态分配内存但随后不释放该内存时,就会导致内存泄漏,可以按如下方式进行内存分配。
int *data;
data = (int *) malloc(8);
char * login= new char(40);
当我们动态分配内存时,该内存来自堆,它是 C++ 使用的系统内存的一部分。 堆栈是变量和函数的获取位置。
如果我们不添加以下代码来清除这些分配,就会出现内存泄漏。 这些泄漏会随着时间的推移而积累,并且根据我们的程序逻辑,可能会导致我们的应用程序失败。
free (data);
delete login;
这是另一种情况,创建一个函数为指针分配八个字节的堆空间。 在 64 位计算机上这将占用 8 个字节。
程序完成执行后,这些字节将不会被释放。
#include <iostream>
using namespace std;
void data_leak() {
double *pointer= new double(28.54);
}
int main()
{
data_leak();
}
在上面的代码块中添加以下循环代码将导致分配一百万字节但从未释放。 我们应该在执行此代码之前保存所有打开的文件。
即使是最先进的操作系统也不会遇到任何问题,但如果我们向 for 循环添加任何额外的指令,我们可能会遇到麻烦。 这就是为什么内存泄漏如此危险。
for(int j=0; j< 150000; j++)
{
data_leak();
}
处理 C++ 中的内存泄漏
首先,我们创建一个名为 func_to_handle_memory_leak()
的函数,然后声明一个整数类型指针来处理内存泄漏,然后使用关键字 new int() 分配一个整数值。
void func_to_handle_memory_leak()
{
int * ptr = new int (6);
}
现在我们使用 delete()
函数来清除之前的内存,避免程序中出现内存泄漏。 所有活动将在函数被调用后立即执行,并且分配的内存将在函数返回之前被释放。
delete (ptr);
在主函数中,我们调用 func_to_handle_memory_leak()
函数。
int main()
{
func_to_handle_memory_leak();
return 0;
}
完整源代码:
#include <iostream>
using namespace std;
void func_to_handle_memory_leak()
{
int * ptr = new int (6);
delete (ptr);
}
int main()
{
func_to_handle_memory_leak();
return 0;
}
C++ 中避免内存泄漏的方法
- 您应该尽可能使用智能指针,而不是手动管理内存。
-
您应该使用
std::string
而不是char\*
。std::string
类速度快如闪电且高度优化; 它在内部负责所有内存管理。 - 在 C++ 中避免内存泄漏的最佳方法是在程序级别进行一些 new 和 delete 调用。 任何需要动态内存的东西都应该埋藏在 RAII 对象中,当它超出范围时释放内存; RAII 在构造函数中分配内存并在析构函数中释放内存,以便保证在变量离开当前作用域时释放内存。
- 应使用 new 关键字分配内存,并应使用 delete 关键字释放内存,所有代码都在这两条指令之间编写。
使用 Valgrind 查找 C++ 中的内存泄漏
内存泄漏是最隐蔽的编程错误之一,因为直到耗尽系统内存并且对 malloc 的调用失败之前,它不会表现为明显的问题。
事实上,如果没有垃圾回收,在处理 C 或 C++ 等语言时,您大约一半的精力可能会花在确保正确释放内存上。
要查看当前支持的工具列表,只需运行 Valgrind,然后选择您希望在执行代码时使用的工具。
使用 memcheck 工具运行 Valgrind 将使我们能够检查准确的内存消耗; 它将提供每个 free 和 malloc 调用的摘要。
为了解释,我们将使用一个名为 memoryleakdemo 的基本程序。
#include <stdlib.h>
int main()
{
char *x = new char[100];
return 0;
}
这将显示有关程序的一些信息,包括没有相应空闲调用的 malloc 调用列表。
% valgrind --tool=memcheck --leak-check=yes memoryleakdemo
我们知道 main 中对 malloc 的调用导致了内存泄漏,但我们无法找到具体的行号。 问题是我们没有使用 GCC 的 -g 选项构建程序,该选项提供调试符号。
如果我们使用调试符号重新编译程序,我们会得到以下输出。
==2022== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2022== at 0x1B900DD0: malloc (vg_replace_malloc.c:131)
==2022== by 0x804840F: main (memoryleakdemo.c:5)
使用 CRT 库查找 C++ 中的内存泄漏
可以使用 Visual Studio 调试器和 C 运行时 (CRT) 库来定位和识别内存泄漏。
激活内存泄漏检测
您必须在应用程序中包含以下语句才能激活调试堆函数。
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
使用这些命令激活调试堆函数后,您可以在应用程序退出点之前插入对 _CrtDumpMemoryLeaks
的调用,以便在程序终止时显示内存泄漏报告。
_CrtDumpMemoryLeaks();
当 _CrtDumpMemoryLeaks
运行时,内存泄漏报告将发送到“输出”窗口的“调试”选项卡。 这是默认行为。
您可以使用 _CrtSetReportMode
函数将报告发送到不同的位置。
CRT库内存泄漏的输出:
如果您的应用程序未声明 _CRTDBG MAP ALLOC
,则 _CrtDumpMemoryLeaks
将生成类似于下面所示的内存泄漏报告。
Detected memory leaks!
Dumping objects ->
{18} normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
如果您的应用程序指定 _CRTDBG MAP ALLOC
,内存泄漏报告将如下所示。
Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\leaktest\leaktest.cpp(20) : {18}
normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
相关文章
C++ 中的余切函数
发布时间:2023/08/23 浏览次数:58 分类:C++
-
本文是关于 C++ 中三角函数的求解。 许多函数在数学库中很容易获得,但有些函数不可用,例如余切函数。对于这样的函数,我们将实现一些高效的用户定义函数。C++ 中的三角函数
C++ 错误 Error: Cannot Call Member Function Without Object
发布时间:2023/08/22 浏览次数:199 分类:C++
-
本文介绍了使用C++进行面向对象编程时,常见的错误“无法调用没有对象的成员函数”。 此外,它还提供了对该错误的潜在修复。修复C++中 Error: Cannot Call Member Function Without Object 错误
C++ std::bad_alloc 异常
发布时间:2023/08/22 浏览次数:119 分类:C++
-
这篇短文讨论了 C++ 中 std::bad_alloc() 异常的主要原因。 它还讨论了使用 C++ 异常处理顺利处理此运行时异常的方法。C++ 中的异常处理
C++ 中的 NULL 未声明错误
发布时间:2023/08/22 浏览次数:157 分类:C++
-
本文将讨论C++中NULL关键字以及未声明NULL的错误。C+ 中的 NULL 关键字 NULL是C++中的常量,用于将指针变量初始化为0。NULL和0可以互换使用。
跳转到 switch 语句中的 Case 标签
发布时间:2023/08/22 浏览次数:103 分类:C++
-
本文将讨论 C++ 中 switch 语句的使用。 此外,还将讨论使用 switch 语句时可能出现的错误,包括跳转到 case 标签错误。C++ 中的 switch 语句
解决C++ 中 - 'Python.h': No such file or directory 错误
发布时间:2023/08/22 浏览次数:118 分类:C++
-
本文将解释如何解决错误“Python.h”:没有这样的文件或目录。 当我们尝试在 C++ 中嵌入 Python 代码,但编译器无法在系统内部找到对 Python 的引用时,通常会发生这种情况。C++ 中 'Python.h': No
C++ 中公共继承、私有继承和受保护继承之间的区别
发布时间:2023/08/22 浏览次数:88 分类:C++
-
在本文中,我们将讨论以下三种类型的继承:Public 继承 Private 继承 Protected 继承 但是,在讨论继承类型之前,我们将讨论 C++ 中基类和派生类的概念。