赞
踩
首先看一下内存是如何分配使用的
- 静态存储区域分配。
内存在程序编译的时候就已经分配好了,也就是说已经编过地址了。- 在堆栈上分配 。
在函数执行期间,函数内局部变量(包括形参)的存储单元都创建在堆栈上,函数结束的时候这些存储单元自动释放。这种内存分配方式的效率很高,一般不存在失败的风险,但是分配的内存容量有限,堆的容量很小,是兆(M)级别的,容易发生栈溢出。- 在堆上分配。
也叫做动态内存分配,程序在运行期间用malloc或new申请任意数量的内存,堆的容量很大,是G级别的,(比如递归时容易发生栈溢出,就可以在堆上模拟实现栈完成递归)。程序员自己决定释放内存的时间(使用free或delete)。动态内存的生存时间由程序员决定,使用非常灵活,但是也非常容易出错。- 使用内存分配的一般原则是:
如果使用堆栈和静态存储就能满足应用要求,就不要使用动态存储。这是因为在堆上动态分配内存需要其他的额外开销。主要原因如下。
4.1 在堆上分配内存时,需要找到符合要求的空闲的连续的字节内存块。特别是在经过多次内存分配之后,堆会变得“千疮百孔”,出现大量的闲散的内存碎片,此时可能需要首先进行碎片合并,然后才能分配成功,在这种情况下内存分配需要很长时间。
4.2如果动态分配失败,需要检查返回值或者捕获异常(try catch),这也需要额外的开销。
4.3动态创建的对象可能被删除多次(比如在这个程序前一部分你已经释放过内存了,但是后来你又忘记了,就又释放了一次),甚至在删除后还会继续使用(还是前面那个例子,已经释放后还要继续使用那块内存空间)。如果发生这种情况,运行时会发生错误,或者出现“内存泄漏”的现象,这些问题是很难避免的,当代码量很大的时候,很容易出现这些意外情况。- 代码段和数据段的区别和联系。
5.1 代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆积组成的。
5.2 数据段:(也被称为数据区、静态数据区、静态区):数据段就是程序中的数据,直观理解就是C语言程序中的全局变量。(注意:全局变量才算是程序的数据,局部变量不算程序的数据,只能算是函数的数据)
1.1 malloc
在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址
。
int* p=(int*)malloc(sizeof(int)*10);//开辟40个字节的空间
1.2 realloc
在已有的基础上对内存空间进行调整,也就是进行扩容
int* p=(int*)malloc(40);//先开辟一块40个字节的空间
p=(int*)realloc (p,400);//再把这块空间扩容到400个字节
1.3 calloc
和malloc的作用相识,开辟好空间之后会把这块空间的数值初始化成0
int* a = (int*)calloc(10, sizeof(int));//开辟一块40个字节的空间,并且把她们初始化成0
释放空间,为了避免野指针,free后要再把指针置成空(NULL)
free(p);//把空间还给操作系统
p=NULL://把指针置成null,防止再次访问
new是c++新引入的操作符,他的主要作用和malloc他们几个一样,都是动态开辟空间,但是new又做了一点小改进。
delete是c++新引入的操作符,他的主要作用和free相似,但是delete不仅会释放空间,还会自动调用析构函数。
内置类型的例子
void test1() { int* p1 = new int; //new一个int大小(4个字节)的空间 int* p2 = new int(10); //new一个int大小(4个字节)的空间,并且初始化成10 int* p3 = new int[10]; //new一个40个字节的空间(10个int类型) int* p4 = new int[]{ 1,2,3,4,5 };//这里大括号里是数组个数,可以省略,就像初始化数组一样 //new一个40个字节的空间的大小,并且初始化他们 delete p1; delete p2; delete[]p3; delete[]p4; }
代码运行结果如下:
自定义类型的例子
class Test { public: Test() { cout << "构造函数"<<" " << this << endl; }; ~Test() { cout << "析构函数" << " "<<this << endl; } private: int x; }; void test2() { Test* p1 = new Test; Test* p2 = new Test[10]; delete p1; delete []p2; }
代码运行结果如下:
可以看出用new开辟空间,不仅会开辟空间,还会自动调用构造函数和析构函数。
总结:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
operator new /operator delete是系统提供的全局函数,而new/delete是一种操作符。
new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。operator new本质是封装了malloc。operator delete本质是封装了free。
⚠️注意:operator new/operator delete不是对new /delete的重载,他们就是独立存在的库函数。
说了这么多,用一句话总结一下:
当你用new开辟空间的时候,new会去调用oparator new函数,然后operator new又会去调用malloc开辟空间。
画个图理解一下
内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
自定义类型
new
调用operator new函数申请空间 在申请的空间上执行构造函数,完成对象的构造
delete
在空间上执行析构函数,完成对象中资源的清理工作 调用operator delete函数释放对象的空间
new T[N]
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请 在申请的空间上执行N次构造函数
delete[ ]
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。