内存分配方式
首先看一下内存是如何分配使用的
- 静态存储区域分配。
内存在程序编译的时候就已经分配好了,也就是说已经编过地址了。 - 在堆栈上分配 。
在函数执行期间,函数内局部变量(包括形参)的存储单元都创建在堆栈上,函数结束的时候这些存储单元自动释放。这种内存分配方式的效率很高,一般不存在失败的风险,但是分配的内存容量有限,堆的容量很小,是兆(M)级别的,容易发生栈溢出。 - 在堆上分配。
也叫做动态内存分配,程序在运行期间用malloc或new申请任意数量的内存,堆的容量很大,是G级别的,(比如递归时容易发生栈溢出,就可以在堆上模拟实现栈完成递归)。程序员自己决定释放内存的时间(使用free或delete)。动态内存的生存时间由程序员决定,使用非常灵活,但是也非常容易出错。 - 使用内存分配的一般原则是:
如果使用堆栈和静态存储就能满足应用要求,就不要使用动态存储。这是因为在堆上动态分配内存需要其他的额外开销。主要原因如下。 4.1 在堆上分配内存时,需要找到符合要求的空闲的连续的字节内存块。特别是在经过多次内存分配之后,堆会变得“千疮百孔”,出现大量的闲散的内存碎片,此时可能需要首先进行碎片合并,然后才能分配成功,在这种情况下内存分配需要很长时间。 4.2如果动态分配失败,需要检查返回值或者捕获异常(try catch),这也需要额外的开销。 4.3动态创建的对象可能被删除多次(比如在这个程序前一部分你已经释放过内存了,但是后来你又忘记了,就又释放了一次),甚至在删除后还会继续使用(还是前面那个例子,已经释放后还要继续使用那块内存空间)。如果发生这种情况,运行时会发生错误,或者出现“内存泄漏”的现象,这些问题是很难避免的,当代码量很大的时候,很容易出现这些意外情况。 - 代码段和数据段的区别和联系。
5.1 代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆积组成的。 5.2 数据段:(也被称为数据区、静态数据区、静态区):数据段就是程序中的数据,直观理解就是C语言程序中的全局变量。(注意:全局变量才算是程序的数据,局部变量不算程序的数据,只能算是函数的数据)
c语言中的内存管理方式
1. malloc/realloc/calloc
1.1 malloc 在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址 。
int* p=(int*)malloc(sizeof(int)*10);
1.2 realloc 在已有的基础上对内存空间进行调整,也就是进行扩容
int* p=(int*)malloc(40);
p=(int*)realloc (p,400);
1.3 calloc 和malloc的作用相识,开辟好空间之后会把这块空间的数值初始化成0
int* a = (int*)calloc(10, sizeof(int));
2.free
释放空间,为了避免野指针,free后要再把指针置成空(NULL)
free(p);
p=NULL:
c++中的内存管理方式
1. new/delete
new是c++新引入的操作符,他的主要作用和malloc他们几个一样,都是动态开辟空间,但是new又做了一点小改进。
- 对于自定义类型,如果使用new,会开辟空间+调用构造函数初始化。
- 对于内置类型,如果使用new,和malloc的作用一样,只会开辟空间。
delete是c++新引入的操作符,他的主要作用和free相似,但是delete不仅会释放空间,还会自动调用析构函数。
- 对于自定义类型,如果使用delete,会释放空间+调用析构函数。
- 对于内置类型,如果使用delete,和free的作用一样,只会释放空间。
内置类型的例子
void test1()
{
int* p1 = new int;
int* p2 = new int(10);
int* p3 = new int[10];
int* p4 = new int[]{ 1,2,3,4,5 };
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不会。
2.operator new/operator delete
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=operator new+构造函数
- operator new=malloc+失败了抛出异常
对于delete也是同理 - delete=operator delete+析构函数
- operator delete=free
画个图理解一下
new/delete实现原理
内置类型
如果申请的是内置类型的空间,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来释放空间
|