1.内存分段问题
栈:又叫做堆栈,非静态局部变量/函数参数/返回值,栈是向下增长的
内存映射:是高效的I/O映射方式用于装载一个共享的动态内存库。用户可以使用系统接口创建共享内存,做进程间通信
堆:用于程序运行中动态内存分配,堆是向上增长的;
数据段:存储全局数据和静态数据
代码段:可执行的代码/只读常量
2.c中动态内存开辟
适合场景:长度在程序运行时才知道数组分配内存空间
为什么使用动态内存开辟:
1.如果数组的空间小于存放数据大小;
2.数组过大,数据较小,内存浪费;
3.输入数据大于数组大小,程序无法给出正常的应答;
malloc/calloc/realloc/free四个函数进行动态内存(内存池)的管理;
1.void *malloc(size_t size)
(1)参数:开辟内存字节数
返回:成功返回一个指向开辟空间起始地址位置的指针
失败返回NULL;
2.void *calloc(size_t num, size_t size)
(1)参数:num 元素数量; size 每个元素所占字节
返回:成功返回指向开辟的空间的首地址的指针(返回之前初始化为0)
失败返回NULL
3。void *realloc(void *pointer ,size_t new_size)
(1)参数:pointer指向空间的首元素地址的指针;开辟空间字节数
返回:成功返回指针,失败返回NULL;
区别:在动态开辟的空间进行增加
(1)在原来的空间上进行增加(返回原来的指针),不初始化
(2)原来内存无法改变大小,重新开辟空间,复制原来的到新的地址,返回新指针,释放原来的空间;
4.void free(void *pointer)
参数:释放空间的指针
动态开辟开辟的空间返回值,一定要进行判空
动态内存开辟常见错误:
1.堆NULL进行解引用操作
2.动态内存访问存在越界
3.对非动态开辟内存free
4.对一块动态开辟空间重复释放
5.释放动态开辟内存一部分
6.动态开辟内存没有释放(内存泄露)
3.c++中内存管理
通过new和delete运算符进行动态内存的管理(开辟空间不需要判空)
eg : int *pl1=new int ; //开辟单个整形空间
int *pl2=new int(10); //初始化
开辟:new +类型 释放 :delete +类型
eg : int *pl3=new int[10]; //开辟连续空间
开辟:new+类型[ ] 释放 delete[ ] +pl3
匹配问题:
new开辟空间,free释放。,不会调用析构函数
如果开辟,释放不匹配,不会调用构造函数,delete会构造出错
内存泄露,代码崩溃:
delete [ ]去释放非new [ ]开辟的空间;
new[ ]开辟的空间没有用delete [ ]释放;
new :
1.申请空间:调用 void *operator new(size_t size)
调用malloc循环申请,直到申请成功,
成功返回地址
失败:检查是否有内存不足应对措施
有:继续malloc
没有:提供异常
2.调用构造函数:初始化对象
delete:
1.调用析构函数:将指针所指向的资源清理掉;
2.释放空间:
调用 void *operator delete(void *p)
调用free释放
new [ ]:
1.申请空间: void *operator new [ ](size_t count=N*sizeof(t)+4(存放开辟对象个数,目的:调用多次析构函数))
operator new (count)
调用malloc循环申请,直到申请成功,
成功返回地址
失败:检查是否有内存不足应对措施
有:继续malloc
没有:提供异常
2.调用N次构造函数为每个对象初始化:
delete[ ]:
1.销毁对象:
a.从空间前四个字节取出析构函数的调用次数N;
b.调用N次析构函数来析构它的N个对象;
2.释放空间:
调用 void *operator delete[ ](void *p)
operator delete p
free p
4定位new表达式:
定义:在已经分配的原始内存空间中调用一个构造函数初始化一个对象
定位new表达式:
new (place_address)type
new(place_address)type(initilizer_list)
place_address必须是一个指针initilizer_list是类型的初始化列表中定位new表达式的实际调用;
使用场景:多用于对象池化处理。
对象池化:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度减少了创建对象造成的开销,而保存对象的容器被称为“对象池”
5.malloc/free&new/delete的区别和联系
直接调用malloc的缺陷:1,系统需要提供分配算法,效率低;
2,额外的空间;
3,内存碎片;
4,内存泄露;
解决:内存池分配
区别: 1. 内存申请的位置:
malloc函数是从堆上分配内存
new(自由存储区)默认在堆上,一般取决于operator new决定位置;
2.成功返回类型:
malloc成功返回void *,需要通过强制类型把void*转换成需要类型
new 成功时返回对象类型的指针,类型与对象匹配,不需要转换,所以new时符合安全类型的操作符
3.失败返回类型 :
malloc 返回NULL
new 返回bac_alloc异常
4.是否需要指定内存大小:
malloc 需要显示指出需要的内存大小
new不需要,编译器会根据类型自动计算
5.是否调用构造函数和析构函数:
malloc不调用构造和析构函数
new需要调用构造函数,delete会调用析构函数
6.对数组处理方式:
malloc直接开辟,如果要动态分配数组,需要手动定义
new分别调用每一个构造函数初始化每个数组元素,释放时调用析构函数,一一对应。
7.是否可以互相调用:
malloc不能调用new
new(operator new &operator delete 需要调用malloc)
8.是否可以被重载:
malloc&free不允许被重载
new中operator new &operator delete可以被重载
9.能直观分配内存:
malloc使用发现不够可以realloc扩充
new不能实现内存的扩充
10.客户处理内存分配不足:
malloc客户不能去编程决定内存不足分配时要干什么,只能返回NULL;
new在operator new抛出异常之前会调用一个用户指定错误处理函数new-handler,它是指针,指向一个没有参数没有返回值的函数,即错误处理函数为了处理错误,客户需要调用set_new_handler,这是一个库函数,参数new_handlerdr指针,指向了operator new无法分配时调用的函数,返回值也是指针,指向set_new_handler被调前正在执行的new-handler函数。
11.类型
malloc/free是标准库函数;
new/delete是c++操作符;
6.特殊要求设计类
1.只能在堆上创建对象;
2.只能在栈上创建对象;
3.类只能创建一个对象;