天天看点

malloc与free使用方法避免内存泄漏

作为第一次在CSDN上写博客的菜鸟级新生,既不想污染大牛们的眼球,却也想不断提高自己,以为达到“仙”的级别,就先做随谈录记录自己在C语言项目中所犯的错误;首先将自己看到的一个很有趣程序猿段子贴上:

有位负责维护的程序员半夜被叫起来,去修复一个出了问题的程序。但是程序的原作者已经离职,没有办法联系上他。这个程序员从未接触过这个程序。在仔细检查所有的说明后,他只发现了一条注释,如下:

MOV AX 723h ;R.I.P.L.V.B.
           

这个维护程序员通宵研究这个程序,还是对注释百思不得其解。虽然最后他还是把程序的问题成功排除了,但这个神秘的注释让他耿耿于怀。说明一点:汇编程序的注释是以分号开头。几个月后,这名程序员在一个会议上遇到了注释的原作者。经过请教后,才明白这条注释的意思:安息吧,路德维希.凡.贝多芬(Rest in peace, Ludwig Van Neethoven)。贝多芬于1827 年逝世,而1827 的十六进制正是723。不得不佩服这些大牛们的思维风暴,与对大师的愐怀性敬仰。下面切入正题:

曾有人问数据库的维护人员”心里有没有点B树?“,就如同对于C/C++程序员来说”心里有没有点分段?“一样;那么内存分段中有个动态区也就是堆区(heap),需要我们自己去维护,所用到的函数调用为:malloc(new),free(delete),很多时候我们对程序的段错误而感到很崩溃,而段错误的产生往往是因为我们对内存的非法访问,段错误的产生往往有四个原因如下:

1.空指针操作  p = NULL ;  *p = 123;现象:直接在本行报错,在驱动移植时这种错误往往也会导致驱动加载时报错;

2.野指针操作  int *p;   *p = 250;

    现象:在本行报错概率较大 

3.指针指向空间已经释放 (其实操作已释放的指针也属于对野指针操作的一种)

    现在:如果内存没有被收回 则不报错 但是有隐患

4.数组越界    int  data[8];   data[8] = 10;  data[-1] = 666;  data[i++] 

    现在:1. 在当前位置报错

         2.在return 位置报错

         3. 在上一层函数的return 位置报错 

而这些段错误在我们进行堆区申请与释放时很容易发生,故将申请堆区的流程进行梳理如下:

  • 堆区空间申请
void *malloc(size_t size);
           
  • 申请内存清零(两个函数任选其一即可)
void *memset(void *s, int c, size_t n); 
      void bzero(void *s, size_t n);  
           
  • 对所申请的内存初始化

释放内存空间

void free(void *ptr);  
           

另外还有一个问题:用malloc 函数申请0 字节内存会返回NULL 指针吗?可以测试一下,也可以去查找关于malloc 函数的说明文档。申请0 字节内存,函数并不返回NULL,而是返回一个正常的内存地址。但是你却无法使用这块大小为0 的内存。好比尺子上的某个刻度,刻度本身并没有长度,只有某两个刻度一起才能量出长度。对于这一点一定要小心,因为这时候if(NULL != p)语句校验将不起作用。

free 函数看上去挺狠的,但它到底作了什么呢?其实它就做了一件事:斩断指针变量与这块内存的关系。比如上面的例子,我们可以说malloc 函数分配的内存块是属于p 的,因为我们对这块内存的访问都需要通过p 来进行。free 函数就是把这块内存和p 之间的所有关

系斩断。从此p 和那块内存之间再无瓜葛。至于指针变量p 本身保存的地址并没有改变,但是它对这个地址处的那块内存却已经没有所有权了(如读写权限)。那块被释放的内存里面保存的值也没有改变,只是再也没有办法使用了。另外务必注意一个malloc对应一个free;也就是说,在程序中malloc 的使用次数一定要和free 相等,否则必有错误。这种错误主要发生在循环使用malloc 函数时,往往把malloc 和free 次数弄错了。

最后我们必须注意一点:既然使用free 函数之后指针变量p 本身保存的地址并没有改变,那我们就需要重新把p的值变为NULL:否则后续使用p时必定出现段错误,因为此时已经释放的p其实是野指针,也有书叫“悬垂指针”。这是很危险的,而且也是经常出错的地方。所以一定要记住一条:free 完之后,一定要给指针置NULL。