内存管理
学习笔记参考:https://chenqx.github.io/2014/09/25/Cpp-Memory-Management/
一、内存分配
C++中,内存分成5个区,和 C 的内存分配不太一样,整体上一致
栈:由编译器在需要时自动分配和释放。通常用来存储局部变量、函数参数、返回地址等
堆:由程序员分配释放,程序员没有释放掉,那么在程序结束后,操作系统会自动回收
自由存储区:是C++中通过 new/delete 动态分配和释放对象的抽象概念
全局/静态存储区:全局变量和静态变量被分配到同一块内存中
常量存储区:存放的是常量,不允许修改
二、动态内存分配和静态内存分配
1. 时间不同
静态分配发生在程序编译和连接的时候
动态分配则发生在程序调入和执行的时候
2. 空间不同
堆都是动态分配的,没有静态分配的堆
栈有静态分配和动态分配
三、堆和栈的区别
1. 管理方式
栈:由编译器自动管理
堆:由程序员控制,容易产生内存泄漏
2. 碎片问题
堆:频繁的 new/delete
会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低
栈:是先进后出的队列,不可能有一个内存块从栈中间弹出
3. 生长方向
堆:生长方向是向上的,也就是向着内存地址增加的方向
栈:生长方向是向下的,是向着内存地址减小的方向增长
4. 分配方式
堆:动态分配,没有静态分配的堆
栈:静态分配和动态分配,静态分配是编译器完成
5. 分配效率
堆的效率比栈要低得多
栈:计算机底层会对栈提供支持,分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行
堆:由C/C++函数库提供
例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间
四、new/delete 和 malloc/free 的区别
https://blog.csdn.net/Hackbuteer1/article/details/6789164
1. new/delete
是C++的运算符,无须指定内存块的大小
内存分配成功时,返回的是对象类型的指针,无须进行类型转换
注意:
1.用new 创建对象数组,那么只能使用对象的无参数构造函数
1 | Obj *objects = new Obj[100]; // 创建100 个动态对象 |
2.用delete 释放对象数组时,留意不要丢了符号‘[]’
1 | delete []objects; // 正确的用法 |
2. malloc与free
是C++/C 语言的标准库函数,需要显式地指出所需内存大小 ,因为并不知道申请的内存是什么类型,只关心内存的总字节数
存分配成功则是返回void ,**需要通过强制类型转换将void指针转换成所需类型**
对于非内部数据类的对象而言,即用户自定义的对象,无法用 malloc/free 动态管理对象(我的理解,自己定义了一个类,可以用new实例化该类,但是不能用malloc为该类分配内存,更不能执行构造函数、析构函数)
3. 对象动态管理
对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free
运算符new能完成动态内存分配和初始化工作,运算符delete能完成清理与释放内存工作
五、内存错误
1. 内存分配未成功
内存分配未成功,就使用
常用解决办法是,在使用内存之前检查指针是否为NULL
2. 内存越界
例如在使用数组时经常发生下标“多1”或者“少1”的操作
3. 内存泄露(指针不存在)
申请的内存空间没有被正确释放,而指向这块内存空间的指针不再存在
含有这种错误的函数每被调用一次就丢失一块内存
4. 释放内存却继续使用
函数 return
了指向栈内存的指针或者引用,但是该内存在函数体结束时就已经被自动销毁了
5. 野指针(指针指向错误的内存)
(1)释放了内存后,指针仍然指向这个已删除的对象,没有将指针设为 NULL
(2)指针未被初始化就被引用,指向一个未知的地址
与空指针不同,野指针无法通过简单地判断是否为 NULL 来避免