c和c++内存管理
c和c++的大致内存分配如图所示:
1.栈又称堆栈—存放非静态局部变量、函数参数、返回值等等,另外栈是向下增长的。
函数调用时创建,函数结束即销毁。(即用即取)
2.内存映射段—高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口
创建共享内存,做到进程中通信。
3.堆—用于程序运行时动态内存分配,另外堆是向上增长的。
4.数据段—用于存储全局数据和静态数据
**5.代码段—可执行代码或只读变量。**然而平时咱们敲的代码(在编译器上)是存在文件里(磁盘)。–这里别搞混啦!
代码在电脑上跑起来时会生成一个.exe文件,运行它时会形成进程,进程会向操作系统申请虚拟进程内存空间。原先在32位操作系统下的32位程序:指针大小为4byte,那么虚拟进程内存空间大约为4G(232byte);而到后来的科技发展到32位操作系统已经满足不了人们的需求,则又有了现在广泛运用的64位操作系统下的64位程序:指针为8byte,那么虚拟进程内存空间大约为232*4G(2^64byte)
【1G=1024MB=1024*1024KB=1024 *1024 *1024byte约等于2^30byte】
第一个空间—(0x00000000)—空指针
c++内存管理方式
new/delete操作内置类型
new申请动态空间
delete释放空间
int main()
{
int* ptr1 = new int;
delete ptr1;
int* ptr2 = new int(1);
delete ptr2;
int* ptr3 = new int[10];
delete ptr3;
int* ptr4 = new int[10]{ 1,2,3,4,5 };
delete ptr4;
return 0;
}
new和delete操作自定义类型
在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会
注意:new和delete必须要配对出现!
第一种情况:我new了一个自定义类型,然而delete了一组自定义类型—没有报错然而死循环了!
第二种情况:我new了一个自定义类型,然而delete了一组自定义类型,但是我把析构函数屏蔽掉了—没有报错正常运行!
第三种情况:我new了一个自定义类型,然而free了一个自定义类型—没有报错正常运行!
但是你并不知道在不报错的时候会发生什么,比如内存泄露等等
第三种情况:我new了10个对象,然而只delete一个对象—会报错
然而我把析构函数屏蔽掉则不会报错—正常运行!
所以我们在使用new和delete的时候一定要匹配阿!!!
operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符**,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete**全局函数来释放空间。
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败则尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
operator delete: 该函数最终是通过free来释放空间的
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
new[]和delete[]
前提:
new的原理:调用operator new函数申请空间;在申请空间上调用构造函数完成初始化;
delete的原理:在空间上调用析构函数完成对对象资源的清理;调用operator delete函数释放对象的空间
所以同样的也有:
new对象[N]的工作原理:newA [N]调用operator new[]函数,实际上operator new[]函数是调用了operator new函数完成N个对象的空间申请;在申请空间上调用N次构造函数完成N个对象的初始化;
delete[N]的工作原理:调用N次析构函数完成对对象资源的清理;delete[N]函数调用了operator delete[]函数,实际上是operator delete[]函数调用了operator delete函数来对对象释放空间;
这里贴一份代码后续要用到噢~
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "调用构造-A()" << endl;
}
~A()
{
cout << "调用析构函数-~A()" << endl;
}
private:
int _a;
};
int main()
{
A* p2 = (A*)malloc(sizeof(A));
if (p2 == nullptr)
{
perror("malloc fail");
exit(-1);
}
new (p2)A(1);
p2->~A();
free(p2);
return 0;
}
定位new(placement-new)
定位new是在已分配的原始内存空间中调用构造函数初始化一个对象。
定位new表达式:new(对象)构造函数(参数:用来初始化,不用初始化可以不写)
比如:new(p2)A(1)
总结以上
| malloc | free | new | delete |
---|
共同点 | 从堆上申请空间,并且需要手动释放 | —— | 从堆上申请空间,并且需要手动释放 | | 不同点 | 是函数 | 是函数 | 是操作符 | 是操作符 | | 申请空间时,需要手动计算大小 | | 不需要计算大小,new后接类型[个数] | —— | | 返回值void*,返回时需要强转 | —— | 后接类型 | | | 申请空间时要判空,申请失败时返回NULL | —— | 不需要判空,但失败会抛异常 | | 申请自定义类型时 | 只会申请开辟空间,不会初始化 | 只会释放空间 | 不仅会开辟空间,还会调用对象的构造函数初始化 | 会调用对象的析构函数完成对象资源的清理然后释放空间 |
内存泄露
内存泄漏指:**因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。**内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。(进程结束前泄露-对某块空间无法使用-比如失去指向这块内存的指针,进程结束后内存还给操作系统)
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现
内存泄漏会导致响应越来越慢,最终卡死。(不怕突然泄露,就怕不知不觉之间泄露小部分空间,长时间累积导致系统变卡顿-温水煮青蛙)
相当于我们生病,不怕急性病(当前医疗环境好的情况下),就怕慢性病(不知不觉就养成癌症),所以我们程序员要坚持每天运动,不要熬夜啦!
以上就是全部内容啦,如果有你对哪点不清晰或者有问题的话,请在评论下留言,我会尽我所能答复哒
|