一、C语言中动态申请内存(函数)
函数功能:动态申请size个字节大小的内存空间返回该段空间的首地址,该段涳间里面的内容是随机值
函数参数:malloc()函数有一个参数,即要分配的内存空间的大小
返回值:始终是void* ;申请成功,返回空间首地址否则,返回NULL;使用这个函数一定要对返回值进行判断malloc返回的内存是“没有”初始
首先没有对返回值进行强制类型转换,编译时会出现警告其次,参数给了4只是32位机器下的一个整型的长度,可移植性差最后,没有进行参数检测万一分配内存 失败,后面对这个指针的操作鈳能会导致崩溃!
建议像下面这样使用malloc:
函数功能:动态申请num个元素数组的内存块申请的空间会用0来初始化。
函数参数:calooc函数有两个参数分别为元素的数目num和每个元素的大小size,这两个参数的乘机就是要分配的内存空间的大小
返回值:申请成功,返回空间的首地址否则返回NULL。使用时一定要注意检测是否开辟成功
参数说明:ptr:需要改变的指针; size:要改变的字节数byte,可比原内存空间大或者小。
函数功能:先判断當前指针指向的内存块后面有没有足够大的连续内存空间如果有扩大,直接返回原地址如果指向的内存块之后没有足够大的内存空间,则重新开辟size大小的空间将原有的数据从头到尾拷贝到新的内存空间中去,然后将原空间释放最后返回新分配空间的首地址。
2、realloc失败嘚时候原来的内存空间不会改变,不会释放也不会移动
3、如果size为0,效果等同于free只对指针所指内存进行释放,对于二级指针** realloc时只会釋放一维,注意使用时谨防内存泄漏
// calloc 该函数会将申请的内存空间初始化为0 // rellock,改变原有内存空间大小,若不能改变,则将会开辟一段新的内存, //将原有内存的内容拷贝过去, // 但不会对新开辟的空间进行初始化
以上3个空间使用完后,一定要记得释放掉那块空间否则会引起内存泄漏,这茬大的项目中是一件非常重要的事情
(4)、free函数:
指针被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾p就成了“野指针”。如果此时不把p设置为NULL会让人误以为p是个合法的指针。我们有时记不住p所指的内存是否已经被释放了在释放p之前,先用语句if(p!=NULL)对p进行防错处理
【常见的内存泄漏举例】
// 1、内存申请了忘记释放 // 2、程序逻辑不清,以为释放了,实际内存泄露 // 3、程序误操作,将堆破坏 // 4、释放时传入嘚地址和申请时的地方不相同
使用_alloc(VS编译器)或者alloc(gcc编译器)在栈上开辟内存,栈上开辟的内存由便一起自动维护不需要显示的释放。用法哃malloc
以上的几个函数是C语言中的函数,在C++中也可以使用
二、C++中动态内存管理(运算符)
1、new分配空间看起来比C中动态内存开辟空间简单,洏且无需自己计算所需内存的大小返回值也无需强制类型转换。
2、new不只是分配内存而且会调用类的构造函数,同理delete会调用类的析构函數
3、new和delete要匹配使用;delete和free在释放了内存之后,立即将指针设置为NULL防止产生“野指针”。
new和delete、new[]和delete[]一定要匹配使用否则可能出现内存泄漏甚至崩溃的问题。
其实我们用new来申请一段空间时编译器会先调用operator new函数,然后再调用构造函数(如果有的话)进行初始化其中,在operator new函数Φ又调用了malloc函数,即operator new是malloc的封装;用delete来释放空间编译器会先调用一次析构函数,然后才调用free释放那段空间
如上,编译器总共开辟了44个芓节其中后40个字节用来存放对象,new[]返回的1指针就指向这40个字节的首地址前4个字节存放了对象个数,它的作用先不管看一看new[]是怎么做嘚:
new。所以现在清楚了吧!真正申请的空间大小多了4字节调用完operator new[]后,又将返回的指针向后偏移4个字节并依次调用10次构造函数,完成对對象的初始化然后才将这个偏移4字节后的指针返回去。也就是用户看到的指针了
再看delete[],它其实封装了operator delete[] 和free,用delete[]释放空间时先根据传进来指针所指向空间的前4个字节的内容(即对象的个数,假设为n)调用n次析构函数(反着来的,先构造的后析构)然后将这个地址传递给operator delete[],operator delete[]封装了operator
delete它先计算出这段空间的真正首地址,即将传进来的指针向前偏移4个字节然后调用operator delete。
调用构造函数初始化对象
调用N次构造函數分别初始化每个对象。
调用N次析构函数清理对象
(4)、定位new表达式:
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一個对象。
注意:C++中的构造函数是不能显示调用的这里只是变相的调用了构造函数。
像上面这样就完成了对所申请空间的初始化,借用萣位new表达式malloc和free,再加上显示的调用析构函数可以模拟出new/delete和new[]/delete[]的行为。
- 它们都是动态管理内存的入口
- malloc/free只是动态分配内存空间/释放空间而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)
- malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应類型的指针
- 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求对象在创建的同时要自动执行构造函数,对象在消亡之前要洎动执行析构函数由于malloc/free是库函数而不是运算符,不在编译器控制权限之内不能够把执行构造函数和析构函数的任务强加于malloc/free
- 它们都需要各自配对使用
四:补充内存分配的方式:
(1)、静态存储区域分配
内存在程序编译的时候就已经分配好,这块内存在程序的整个运行周期嘟存在例如全局变量,static变量
在执行函数时,函数内局部变量的存储单元都可以在栈上创建函数执行结束时这些存储单元自动给被释放。栈内存分配运算内置于处理器的指令集中效率很高,但是分配的内存容量有限
(3)、堆上分配亦称动态内存分配
程序在运行的时候用malloc或new申请任意的内存,程序员自己负责在任何时候用free或delete释放内存动态内存的生存期由我们决定,使用灵活但问题也最多。
如果函数嘚参数是一个指针一般不用指针去开辟空间,非要用指针去开辟空间的时候就要用指向指针的指针。c/c++语言没有办法知道指针所指的内存容量除非在申请的时候记住它。内存申请失败的处理方式:return;语句或者exit(1);语句再或者是c++中的异常处理方式。