新手浅析:从“exit()会内存泄漏吗”到“怎么样避免内存泄漏”

文章探讨了在C++编程中,使用exit()强制中断程序是否会导致内存泄漏。通过测试,作者发现操作系统在进程结束时会回收内存,但建议程序员应该跟踪并适时释放内存以减少内存泄漏。文章还提到了矩阵乘法的示例,讨论了如何通过重载赋值运算符来完善内存管理,并强调了良好的内存管理习惯对于程序稳定性和用户体验的重要性。

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

本人是新手,如果说得不对还请多多包涵和指点。

最近心血来潮想要写个项目,期间遇到了这样一个问题:
我需要在异常访问的时候强制中断程序,那么,使用exit()到底会不会导致内存泄漏?

内存泄漏是什么,这个问题我想不必多说,何况我也不是什么权威,说错了怕贻笑大方,在此省略;我本来是想在网上寻找答案,但是找遍了网上,也不见得能给出一个较为妥当的答案(为此我甚至去了cplusplus.com,可能是我英语不过关,硬是找不到好的答案);一怒之下,我只好自己写代码来测试(以身试险),闲话不多说,让我们看看我测试用的代码:

#include <cstdlib>
int main(int argc, char* argv[])
{
	long long *a = new long long;
	for (unsigned i = 0; i < 53687091; i++)
		a = new long long;
	system("pause");
	exit(1);
	return 0;
}

关于测试代码,在这里我要做出几点概述:

1.之所以没有iostream或者cstdio,是因为根本不需要输入输出什么东西;
2.如果直接使用long long a[],可能会触发C++实现的某些我不了解的内存回收机制,导致异常退出时强制回收空间;还可能会发生一个情况,因为typename varname[]是向系统申请一段连续的空间,如果内存中碎片很多的情况下,可能直接就申请空间失败;
3.对于配置不好的计算机,请适度调整循环次数,否则可能导致无法预料的情况发生(如死机、崩溃);
4.申请这么多次,需要0.5s以上的时间很正常,请耐心等待;
5.我是Linux环境入门的C,习惯了int main(int argc, char* argv[])的写法,大家可以改成自己想要的(但在我的编译环境下是不予通过void main()的)
0.请不要在任何项目中模仿该代码;

OK,代码准备完毕,测试开始:

开始前:


我的电脑配置也是一般般,总内存4GB,可用内存大概2.37GB

循环完成后:


虽然是意想之中的,但是还是被吓了一跳呢。
此时程序并没有退出,需要我们摁下任意键,然后执行exit()强制退出;

于是我摁下了回车键,任务管理器的答复是:


完美!

如果Windows能办到,相信Linux不会比Windows差的,甚至更好也说不定;

这说明了一个规范的操作系统有良好的内存回收机制,对于一个进程申请的所有内存页面都有一个标记,当进程被结束掉的时候,系统会自动释放掉该进程申请的内存页面,无论是被跟踪的内存还是已经丢失的内存(相对于该进程而言)。

我想,系统的反射机制不可能如此脆弱,能被常见的问题给摧毁。一个进程如果不被中止(如后台进程之类),可能会一直持续产生内存泄漏和内存碎片,但是一旦该进程被终止,其申请的内存页面也将关闭。

但是,我们设计的大部分项目,都有一定的运行时间,如果造成众多的内存泄漏,必然会导致不可预料的事情发生;最好的方法是,跟踪每一个申请的内存页面,在恰当的时候将它结束掉,说到这个,我就想起了以前写的一段代码,映像深刻啊:(这个是我在学线性代数的时候写的,当时是为了验证我做的矩阵相乘作业的正确性)

PS:为了节约篇幅,我将不重要的代码全部用"..."代替;

#include <iostream>
template <class TYP_FR_MTRX>
class matrix
{
public:
	...
//重头戏来了
	matrix<TYP_FR_MTRX>& operator * (const matrix<TYP_FR_MTRX>& mltplr) const
	{
		matrix<TYP_FR_MTRX>* answer = new matrix<TYP_FR_MTRX>(this->height, mltplr.length, nullptr);
		...
		return *answer;
	}
private:
	unsigned height;
	unsigned length;
	TYP_FR_MTRX* detail;
};

说明三点:

1.如果矩阵中只需用到整数,我就可以要matrix<int>来满足需求,我之所以写成模板类而不是一个具体的类,目的就是可以随意使用浮点型和整型(同时能锻炼模板类的书写格式)
2.貌似模板这个东西一直是C++的心头大患,如果把模板类中的函数写到别的文件,貌似在很多编译器下连接出错,所以我直接写在一起了;
3.因为是给自己用,所以没必要判断什么到底能不能相乘啊之类的;

矩阵乘法中,返回值也是matrix类的一个实例,那么它就必定带有:两个unsigned int大小的空间,一个指针型大小的空间,和指针访问的一片空间;如果此时,我们用一个matrix<type-name>来接收它的返回值,那么析构的时候,只会析构掉其自己的两个unsigned int大小的空间,一个指针型大小的空间,及其指针访问的一片空间(也就是我们new出来的那个)。

其中,answer所new出来的两个unsigned int大小的空间,一个指针型大小的空间没有被析构掉,这很微小,但是却不容忽视。

我当时使用的方法是,matrix<type-name>* p = &(a * b);其中a与b是与*p同type-name的两个实例;这样,我们delete p,可以发现在operator*()中构造的实例被完整的析构了;(测试方法:在构造函数里打印一行"构造",在析构函数里打印一行"析构")

现在想想,当时还真的有点蠢偷笑,实际上我们可以通过重载'='号来完美完成这个内存回收机制:

matrix<TYP_FR_MTRX>& operator = (matrix<TYP_FR_MTRX> const& rghnd)
{
	const unsigned sz_mtrx = rghnd.length * rghnd.height;
	if (this->detail != nullptr)
		delete[] this->detail;
	if (rghnd.length != 0 && rghnd.height != 0 && rghnd.detail != nullptr)
	{
		this->height = rghnd.height;
		this->length = rghnd.length;
		this->detail = new TYP_FR_MTRX[sz_mtrx];
		for (register unsigned i = 0u; i < sz_mtrx; i++)
			this->detail[i] = rghnd.detail[i];
	}
	else
	{
		this->height = this->length = 0;
		this->detail = nullptr;
	}
	return *this;
}
这样,当用函数返回值的时候,就不用担心其局部实例被析构掉,让我们指向一片非法内存了,而且这也避免了两个matrix<>指向同一片空间,一举两得。

在写项目的时候,就可以类似的这么去用,尽量确保每一片申请的内存空间都能被访问,即使因为各种原因无法完全确保,也需要我们将内存泄漏和内存碎片问题降到最低。至于原因,我想大家都知道,没有哪个用户喜欢一点开就死机的程序。

注:
1.本文原创,若转载请注明原作者和出处
2.实验环境:Windows 7旗舰版(正版)、G++编译器(GNUC++Compiler使用Code::Blocks作为IDE环境)
3.对malloc()也同样进行了测试,请C语言程序员放心

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值