C++进阶:内存管理

C++ 内存管理

个人在学习C++的基本STL用法后,虽然对于C++各项功能都有了一些了解,但是却无法形成具体系统影响,导致容易遗忘。这里进行西嘎嘎进阶内容整理,主要是内存管理、继承组成、多态虚函数。本章主要说明内存管理。

简介

在C++语言中,内存主要分为:栈区、堆区、数据区、代码区。

栈区(stack)

——生命周期很短,仅为单次调用过程,由编译器自动分配 ,存储默认存储类型或auto修饰的局部变量,存放函数的参数值,局部变量的值等,函数执行结束时自动释放。内存分配运算内置于处理器的指令集中,效率高,但内存空间有限,前进后出。
ps.声明一个局部变量:int b;——自动在栈开辟int大小空间。
注意:初学者错误返回局部变量指针,但指针所指向局部变量已退栈,会发生难以预料的错误。

堆区(heap)

——动态内存分配,一般由程序员分配释放malloc-free, 若程序员不释放,容易导致内存泄露,只有程序结束时由OS回收,分配方式类似于链表。
ps.程序员申请p1=(char*)malloc(10),10*sizeof(char)空间在堆区,指针p1在栈区。

数据区(data):细分为自由存储区、全局静态存储区、常量存储区。

(1)静态存储区(static)——是一个抽象概念,包括BSS段和数据段。BSS存储未初始化或初值为0的全局变量和静态变量,表现为占位符,无分配数据空间,只记录空间大小;数据段存储初始化的全局和静态变量,数据生命周期很长,且只初始化一次,程序结束后由系统释放。尽管静态区由两个段组成,但是在程序链接并载入内存后,不存在物理区分。
(2)自由存储区——C++基于new-delete操作符的一个抽象概念,对应内存管理空间就是自由存储区,其实际可以是堆区、动静态存储区,主要看在哪里操作。
(3)文字常量区 ——存放只读数据,包括数值常量、字符串常量,const全局常量,还有格式字符串,但其对应的指针在上述1、2、3区域,编译时确定,程序结束后由系统释放,运行时受系统保护,强行修改会导致segmentation fault。此外,对于相同的字符串常量,编译时被去重只保留一份,其指针或string类型参数指向它。
ps.static int i=10;i处于静态存储区;printf(“Hello world%d\n”);其中的“~”就是格式字符串。

程序代码区

——存放函数体等指令编辑的二进制代码以及立即数等。
ps.int a = 10中的“10”是立即数,存放于代码区。

常量区、静态区以及代码区,由编译确定;堆区、栈区、自由存储区空间,在运行时进行变动。

栈空间管理

栈由系统自动分配,速度较快,但程序员无法控制。
在Windows下,栈是一块连续内存空间,其生长方向向下,向着内存地址减小方向增长,由高向低地址扩展。其栈顶地址和栈尾地址以及栈的最大容量是系统预先规定好的,在WIN下,其大小是2M(也有说是1M,总之编译时已确定),若申请空间大于剩余空间,将提示overflow栈溢出。
栈的作用过程: 在函数调用时,第一个进栈的是函数调用语句的下一条可执行语句的地址;然后是函数的各个参数,参数由右往左入栈的;然后是函数的局部变量,但静态变量不入栈;当函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向下一条可执行语句,主函数继续向下运行。
注意:栈区根据函数生命周期进行空间管理,不会出现内存碎片。在这里插入图片描述

堆空间管理

操作系统预留出一部分不连续内存区域,用链表来存储内存地址,即是堆结构,地址由低向高地址扩展。堆的大小受限于计算机系统中有效的虚拟内存。
**堆作用过程:**当系统收到申请时,会遍历链表,寻找第一个空间大于所申请空间的堆结点,从空闲链表中删除,并将对应空间分配给程序,另外在这块内存空间的首地址处记录本次分配空间大小,从而确保delete/free语句正确释放空间。由于所安排的堆结点的大小不一定正好等于申请额度,系统会自动的将多余部分归入链表。
**堆内容安排:**堆的头部用一字节存放堆的大小,其余内容由程序员安排。
堆由new分配内存,速度较慢,且容易产生内存碎片,不过用起来最方便。在WIN下,最好用VirtualAlloc分配内存,不在堆或栈区域,而是直接在进程地址空间中提取一块内存,虽然使用最不方便,但是速度快,也最灵活。

如下图所示,堆空间一共有1024字节,目前还有空闲字段100、50、44字节,假设此时申请150字节空间,找不到一个连续空间≥150字节,申请失败,即为内存碎片问题。

注意:堆空间频繁的new/delete会造成内存空间的不连续,产生内存碎片,降低程序效率。
在这里插入图片描述

数据区管理

内存中变量存储在数据区,数据区可分为静态存储区与动态存储区,自由存储区。自由存储区不概述。
静态存储是指在程序运行期间给变量分配固定存储空间的方式,如全局变量存放在静态存储区,程序运行时分配空间,运行完释放。
动态存储是指在程序运行时根据实际需要动态分配存储空间的方式。如形式参数存放在动态存储区,函数调用时分配空间,调用完释放。
静态存储变量在编译时初始化,默认初值为0或空字符;动态存储变量,若不赋初值,则数值不确定。

存储类别

在C++中,具体的存储类别有自动(auto)、寄存器(register)、静态(static)及外部(extern)。静态存储类别与外部存储类别变量存放在静态存储区,自动存储类别变量存放在动态存储区,寄存器存储类别置于寄存器。下面是详细的解释:
auto存储类指明符——局部作用域的变量,具有局部(自动)生成类别,由于它是所有局部作用域变量说明的缺省存储类指明符,使用得很少。所有在函数内部定义的变量都是局部变量,其作用域只在函数内部。
register存储类指明符——作用效果等价于auto,只能用于局部变量和函数参量说明,编译程序将尽可能为该变量分配CPU内部寄存器作为变量存储单元,以加快运行速度。注意,寄存器与存储器不同。寄存器一般在CPU内部,而存储器指外部存储器,CPU内部寄存器运算速度很高,当寄存器已分配完毕,则自动分配给外部内存。
static存储类指明符——表示变量具有静态生成期,静态变量不被自动释放。当回到该作用域后又可以继续使用这个static变量值。
extern存储类指明符——一般用在工程文件中。一个工程包含多个程序文件,当某个变量在一个程序文件中定义了后,如果在另一程序文件中予以定义,就会出现重复定义的错误,extern存储类型指明符可以指出在文件外部已定义变量,其作用域是整个程序。
此外还有thread_local和mutable。
mutable存储类指明符——仅使用于类的对象,允许对象成员代替常量(即mutable成员可通过const成员函数修改)。
thread_local存储类指明符——thread_local声明变量仅可在其线程上访问,变量生命周期同线程周期。thread_local仅应用于数据声明和定义,不能用于函数声明或定义。

变量生命周期

1.变量可以存储在内存的不同地方,这依赖于它们的生成期。在函数上部定义的变量(全局变量或static外部变量)和函数内部定义的static变量,其生存期就是程序运行的全过程。这些变量被存储在数据段(Data Segment)中。
2.在函数内部定义的auto变量(没有用关键字static定义的变量)的生成期从程序执行到其所在的作用域开始,到程序离开该程序块结束。函数参数变量只在调用期间存在,这些变量被存储在栈(stack)中。
3.当用malloc/new等函数给指针分配一个地址空间的时候,这个分配内存块位于堆(heap)空间中,其生命周期由程序员决定。

注意

1.静态数据,只初始化一次,它的空间数据在编译期间被初始化,逻辑地址在链接期间固定。
2.自由存储区是C++通过new/delete动态分配释放对象的抽象概念。基本所有C++编译器默认使用堆实现自由存储,其全局运算符new/delete会按照malloc/free方式被实现,此时借由new运算符分配的对象,可以说其在堆上,也可以说其在自由存储区;若通过重载操作符,改用其他内存操作实现自由存储,例如全局变量的对象池,这时自由存储区区别于堆。我们所需要记住的就是:
堆是操作系统维护的一块内存,而自由存储区是C++通过new/delete动态管理的抽象概念,两者不等价。

代码区管理

未找到可补充内容。

参考

堆和栈的区别
C++内存管理
C++内存管理
C++存储类别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值