C++内存分布及用法
作者:一个热爱学习的深度渣渣
一、内存基础
1、内存分布
通过下面一张图看看C++的内存分布:
栈区:由编译器自动分配与释放,存放为程序运行时函数分配的局部变量、函数参数;栈内存分配运算内置于处理器的指令集中,效率很高,但是分配内存的容量有限;
堆区:由new
、malloc
分配的内存块,释放由应用程序控制,不需要编译器释放;如果程序员没有对该内存进行释放,程序结束后系统自动回收,堆的地方比栈大很多;
静态区:存放的是static
的静态变量和一些全局变量,特点是只读、大小固定;静态变量和全局变量的存储期是一起的,一旦静态区的内存被分配,要一直等到程序全部结束后才释放;
2、栈区与堆区的区别
1、分配方式不同:栈区系统分配系统回收;堆区由程序员手动申请,需要求程序员自行回收,如果没有回收,系统在程序结束后会进行回收,这种情况会造成内存泄漏;
2、生命周期不同:栈区生命周期是系统分配到系统回收,也就是在大括号内;堆区是从申请到释放;
3、效率不同:主要原因是地址空间是否连续,栈区地址空间是连续的,效率会高一些;堆区地址空间不连续,需要遍历链表才能找到最近的地址,效率会低一些;
4、内存碎片:堆区容易产生内存碎片,栈区不会;
5、生长方向不同:栈区申请空间的地址(表示地址的八个十六进制数)是从大到小的,堆区申请空间地址是从小到大的。栈区是先进后出的原则,类比栈结构的特点;
- 栈区特点:更好的局部性,对象自动销毁;
- 堆区特点:运行期动态扩展,需要显示释放;
注意点:申请的空间是在堆区,变量本身是在栈区!
二、内存分配
1、内存分配方式
可操作的内存分配:
- 静态存储区分配:
static
静态变量、全局变量; - 栈上分配:局部变量;
- 堆上分配:
new
、malloc
进行内存分配;
不可操作的内存分配:
内核区、代码区、局部变量的分配也属于系统分配;
2、new的用法
C++中通常使用new
、delete
来构造和销毁对象;
使用new创建对象,返回的是对象的首地址,需要用指针接收:
int *y = new int(2); std::cout << *y << std::endl;
对象的构建和销毁分为两步:分配内存、所分配内存上构造对象(销毁与之类似);
new的几种常见形式:
new int(2)
:构造单一对象、new int[5]:构造数组;nothrow new
:标准库定义,解决内存分配失败异常的问题;placement new
:使用已经创建的内存,跳过分配内存;new auto
;
3、delete用法
根据分配的是单一对象还是数组,采用相应的方式销毁;
int *y = new int[3]; delete[] y;
不能delete
一个非new返回的内存(也就是栈内存);delete nullptr
是可被允许的;
同一块内存不能delete
多次;
4、new与malloc的区别
new
不需要指定分配多大,malloc
使用的时候必须指定大小;new
的底层实现就是malloc
,两者都必须释放内存,不否则容易造成野指针或内存泄漏。需要注意一点,释放内存后需设置相关指针为空指针;
总结:
- 属性:
new
为关键字(编译器),malloc是库函数(需引入头文件); - 参数:
new
无需指定大小,malloc需指定大小; - 返回类型:new返回对象指针,
malloc
返回void*; - 对于自定义的类:new会调用构造和析构函数,malloc不会调用构造和析构函数;
- 分配失败:new会抛出异常,malloc会返回空;
5、内存泄漏
是指由于疏忽或错误造成程序未能释放掉不再使用的内存的情况,内存泄漏并非指内存在物理上的小时,而是应用程序分配某段内存后,由于设计错误,失去该段内存的控制从而造成内存浪费;
可能的原因:
- 1、申请后未释放(最常见)
- 2、
void*
指针的释放 - 3、
new[]
回收时没有用delete[]
,数组的回收要注意
三、内存拓展
1、内存概念
计算机重要部件之一,是外存与CPU进行沟通的桥梁。计算机所有程序都是在内存运行的,因此内存的性能对计算机的影响非常大。内存也称为内存储器和主存储器,作用是暂时存放CPU的运算数据,以及与硬盘等外部存储器交换的数据;
寻址空间:保存内存地址的多少,通常我们说的4G内存,就表示计算机能保存2的32次方个地址,也就是能找到这些地址上的二进制信息;
寻址能力:每个地址里能存多少个bit
,现在的计算机大多数是16位机器了;
2、虚拟内存
使得系统运行实际的内存空间比想象的大得多,虚拟内存是可以远大于物理内存的,同时主要为了使程序运行的时候可以不限制于只访问内存大小,可以通过虚拟内存地址去访问磁盘空间;
每一个进程虚拟内存都是独立的,独立的享有计算机的内存。虚拟内存地址的大小是与地址总线位数相关,物理内存地址的大小是与物理内存条的容量与磁盘容量相关。
四、思考
1、代码中的b属于栈区还是堆区?
void fun() { int *b = new int[14]; }
b是在栈区的变量,由于b是一个局部变量,随着函数域 的结束被释放,不需要程序员自行释放,尽管b使用new进行初始化,还是可以认为分配在栈区;
总结:
本次系统的从内存的基础概念到内存分配进行了讲解,内存是我们开发中最重要的一部分,往往逻辑上的错误就会造成内存泄漏,导致程序无法运行。或者一些分配内存的方式不够细心,也会造成冗余内存的使用。在目前的很多嵌入式板子上,针对内存的接口是必备的,往往也都是基于malloc
修改;
还有一点需要注意,不管任何机器上运行程序,操作的都是虚拟内存,内部通过页表定位到对应的物理内存。关于硬件方面的本质,如果做嵌入式端的话需要深入研究。
到此这篇关于C++内存分布及用法的文章就介绍到这了,更多相关C++内存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!