论C语言的malloc,calloc,new,realloc,alloca的机制和区别

最近笔试老是遇到关于C语言的malloc,new之类的内存机制问题,作为一个做java开发的程序员不免有些郁闷,驾驭不了。乘空闲下来的这些时间,好好整理下C语言中各个内存函数的简单机制,作用和区别:

C语言内存分配方式
  (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多

C语言的内存申请函数
C语言跟内存申请相关的函数主要有 alloca,calloc,malloc,free,realloc,sbrk等.
  其中alloca是向栈申请内存,因此无需释放. malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间.
  calloc则将初始化这部分的内存,设置为0. 而realloc则对malloc申请的内存进行大小的调整.申请的内存最终需要通过函数free来释放. 而sbrk则是增加数据段的大小;
  malloc/calloc/free基本上都是C函数库实现的,跟OS无关.C函数库内部通过一定的结构来保存当前有多少可用内存.如果程序malloc的大小超出了库里所留存的空间,那么将首先调用brk系统调用来增加可用空间,然后再分配空间.free时,释放的内存并不立即返回给os,而是保留在内部结构中. 可以打个比方: brk类似于批发,一次性的向OS申请大的内存,而malloc等函数则类似于零售,满足程序运行时的要求.这套机制类似于缓冲.

malloc的机制及与calloc的区别

malloc的底层机制是:操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

malloc()函数有一个参数,即要分配的内存空间的大小:
  Void *malloc(size_t size);

这里,返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。而malloc()函数为动态内存分配,原型:extern void *malloc(unsigned int num_bytes); 分配长度为num_bytes字节的内存块。如果分配成功则返回指向被分配内存的指针,分配失败返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。动态创建的对象可以用初始化变量的方式初始化:int *pi=new int(100); //指针pi所指向的对象初始化为100,string *ps=new string(10,’9’);//*ps 为“9999999999”

如果不提供显示初始化,对于类类型,用该类的默认构造函数初始化;而内置类型的对象则无初始化。也可以对动态创建的对象做值初始化:int *pi=new int( );//初始化为0,int *pi=new int;//pi 指向一个没有初始化的int,string *ps=new string( );//初始化为空字符串 (对于提供了默认构造函数的类类型,没有必要对其对象进行值初始化)。

  calloc()函数有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小:
  void*calloc(size_t numElements,size_t sizeOfElement);
    如果调用成功,函数malloc()和calloc()都将返回所分配的内存空间的首地址

malloc() 函数和calloc()函数的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由malloc()函数分配的内存空间原来没有被使用过,则其中 的每一位可能都是0;反之,如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,使用malloc()函数的程序开 始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题。
  calloc() 函数会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为零;如果你是 为指针类型的元素分配内存,那么这些元素通常(但无法保证)会被初始化为空指针;如果你是为实数类型的元素分配内存,那么这些元素可能(只在某些计算机 中)会被初始化为浮点型的零。
  malloc() 函数和calloc()函数的另一点区别是calloc()函数会返回一个由某种对象组成的数组,但malloc()函数只返回一个对象。
为了明确是为一 个数组分配内存空间,有些程序员会选用calloc()函数。但是,除了是否初始化所分配的内存空间这一点之外,绝大多数程序员认 为以下两种函数调用方式没有区别:
  calloc(numElements ,sizeOfElement);
  malloc(numElements *sizeOfElement) ;

当你在calloc()函数和malloc()函数之间作选择时,你只需考虑是否要初始化所分配的内存空间,而不用考虑函数是否能返回一个数组。

当程序运行过程中malloc了,但是没有free的话,会造成内存泄漏.一部分的内存没有被使用,但是由于没有free,因此系统认为这部分内存还在使用,造成不断的向系统申请内存,是的系统可用内存不断减少.但是,内存泄漏仅仅指程序在运行时,程序退出时,OS将回收所有的资源.因此,适当的重起一下程序,有时候还是有点作用.

malloc分配内存时需要注意的问题:

1)申请了内存空间后,必须检查是否分配成功。
2)当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。 
3)这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
4)虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查。

5)一旦删除了指针所指的对象,立即将指针置为0,这样就非常清楚的指明指针不再指向任何对象。(零值指针:int *ip=0;)

malloc和new的区别

1)malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

2)对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

3)内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。

4)new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的,而malloc返回的都是void指针。

希望这篇文章能给在学C的你或者在找工作的你一些帮助微笑转载请注明:http://blog.csdn.net/wws199304/

最后再讨论下一道题:

malloc的内存free后什么情况内存不被os回收(15腾讯笔试题)

1)来自知乎大神的回答:

若是glibc(C语言运行库),你所free掉的内存,不一定会马上被OS回收,这是合理的。试想一下,你每次free掉的内存都还给OS的话,尤其是在小字节的情况下,那么造成的情况,就是一大块的内存被你弄的千疮百孔,也就是说一块内存,里面有很多gap。而在操作系统的虚拟内存管理中,更是管理着的是固定大小的内存,如4K,那你还给我1 Byte,OS显然是很尴尬的。于是为了避免这样的问题,那么内存管理一般会有一个free block list,free掉的东西就放在这里来。那么你可能会释放很散乱的内存过来,没关系,我们在这里会尝试合并这些散乱的block,而malloc首先找的也是free block list,而非从OS申请新的内存。那么此时如果找到了一块儿合适的自然最好,如果找到的是比要的更大,那么一部分malloc,另一部分放回去。而上面有同学提到了小内存的问题,而这也是free block list在头部会有一些所谓的administrative data,所以用标准的malloc和free管理小内存是不高效,因为越小越容易造成gap。

简单点讲,就是free后的内存有时不会给os,而是留给程序,下次申请内存时就申请这块内存。


      

你可能感兴趣的:(c,malloc,内存泄露,C内存管理,C内存申请)