C++内存
一个热爱学习的深度渣渣 人气:0一、内存基础
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
修改;
还有一点需要注意,不管任何机器上运行程序,操作的都是虚拟内存,内部通过页表定位到对应的物理内存。关于硬件方面的本质,如果做嵌入式端的话需要深入研究。
加载全部内容