C语言内存泄漏剖析

大家都知道,在堆上分配的内存,如果不再使用了,就应该及时释放,以便后面其他地方可以重用。而在 C 语言中,内存管理器不会自动回收不再使用的内存。如果忘了释放不再使用的内存,这些内存就不能被重用了,这就造成了内存泄漏,内存泄露只可能出现在堆上。
内存泄漏几乎是很难避免的,但是掌握了一定方法还是能很好的降低内存泄露的风险。
也许对一般的应用软件来说,这个问题似乎不是那么突出与严重。一两处内存泄漏通常并不致于让程序崩溃,也不会带来逻辑上的错误,而且在进程退出时,系统会自动释放所有与该进程相关的内存(共享内存除外),所以内存泄漏的后果相对来说还是比较温和的。但是,量变会导致质变,一旦内存泄漏过多以致耗尽内存,后续内存分配将会失败,程序就可能因此而崩溃。
在常见情况下,内存泄漏的主要可见症状就是罪魁进程的速度减慢。原因是体积大的进程更有可能被系统换出,让别的进程运行,而且大的进程在换进换出时花费的时间也更多。即使泄漏的内存本身并不被引用,但它仍然可能存在于页面中(内容自然是垃圾),这样就增加了进程的工作页数量,降低了性能。

下面展示了一些导致内存泄漏的常见场景。

1) 指针重新赋值

看下面一段示例代码:

1.char * p = (char *)malloc(10);
2.char * np = (char *)malloc(10);

其中,指针变量 p 和 np 分别被分配了 10 个字节的内存
如果程序需要执行如下赋值语句:

1.p=np;

这时候,指针变量 p 被 np 指针重新赋值,其结果是 p 以前所指向的内存位置变成了孤立的内存,它无法释放,因为没有指向该位置的引用,从而导致 10 字节的内存泄漏。
因此,在对指针赋值前,一定确保内存位置不会变为孤立的。

2) 错误的内存释放

假设有一个指针变量 p,它指向一个 10 字节的内存位置。该内存位置的第三个字节又指向某个动态分配的 10 字节的内存位置,
如果程序需要执行如下赋值语句时:

1.free(p);

很显然,如果通过调用 free 来释放指针 p,则 np 指针也会因此而变得无效。np 以前所指向的内存位置也无法释放,因为已经没有指向该位置的指针。换句话说,np 所指向的内存位置变为孤立的,从而导致内存泄漏。
**因此,每当释放结构化的元素,而该元素又包含指向动态分配的内存位置的指针时,应首先遍历子内存位置(如本示例中的 np),并从那里开始释放,然后再遍历回父节点,**如下面的代码所示:

1.free(p->np);
2.free(p);

3) 返回值的不正确处理

有时候,某些函数会返回对动态分配的内存的引用,如下面的示例代码所示:

1.char *f()
2.{
3.    return (char *)malloc(10);
4.}
5.void f1()
6.{
7.    f();
8.}

很明显,函数 f1 中对 f 函数的调用并未处理该内存位置的返回地址,其结果将导致 f 函数所分配的 10 个字节的块丢失,并导致内存泄漏。

4) 在内存分配后忘记使用 free 进行释放

最后,要避免这些内存相关的问题导致的内存越界与内存遗漏等错误,可以参考如下几点进行:
确保没有在访问空指针。
每个内存分配函数都应该有一个 free 函数与之对应,alloca 函数除外。
每次分配内存之后都应该及时进行初始化,可以结合 memset 函数进行初始化, calloc 函数除外。
每当向指针写入值时,都要确保对可用字节数和所写入的字节数进行交叉核对。
在对指针赋值前,一定要确保没有内存位置会变为孤立的。
每当释放结构化的元素(而该元素又包含指向动态分配的内存位置的指针)时,都 应先遍历子内存位置并从那里开始释放,然后再遍历回父节点。
始终正确处理返回动态分配的内存引用的函数返回值。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值