【C++面试篇】C++的内存分区

322 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

概述

C++中将程序使用的内存分为五个部分,从低地址向高地址分别是:代码区、常量区、全局/静态存储区、堆区、栈区

img

下面将对这五个部分做介绍,重点集中在堆区和栈区

代码区

代码区顾名思义就是存放着我们程序运行时使用的代码,程序编译后生成的二进制文件在运行的时候就会加载到这里

代码区是只读的,避免程序的指令被意外修改

常量区

常量区存放着程序中的常量,如被const修饰的变量、字符串常量等等

注意:const修饰的局部变量的存放在栈区的

全局/静态存储区

存放着全局变量和静态变量,在程序运行结束后由编译器自动释放

在C语言中会将该区分为两个段:

  • .bss段:存放未初始化的全局/静态变量
  • .data段:存放初始化的全局/静态变量

但是在C++中不再区分,因为对于全局变量和静态变量都会自动初始化

堆区

由程序动态申请释放的内存空间,由程序员进行管理,如果不手动释放,程序运行结束后会由系统回收

栈区

存放函数的局部变量、函数参数等,由操作系统进行管理

堆与栈的区别

  • 管理方式

    • 堆中的资源由程序员管理
    • 栈中的资源由编译器自动管理
  • 空间大小

    • 堆是不连续的内存区域,大小受限计算机系统中有效的虚拟内存,空间比较大
    • 栈式连续的内存区域,大小是操作系统预定好的,wins下是2M
  • 长方向

    • 堆向高地址方向增长
    • 栈向低地址方向增长
  • 内存碎片

    • 堆:频繁的new/delete会造成内存空间不连续,造成内存碎片
    • 栈是先进后出的连续内存区域,不会造成内存碎片
  • 分配方式

    • 堆是动态分配的
    • 栈有静态分配和动态分配
  • 分配效率

    • 堆由函数库提供,在分配堆内存时需要一定的算法寻找合适大小的内存,如果查找不到需要通过系统调用去分配,效率比较低
    • 栈是机器系统提供的数据结构,计算机在底层对栈提供支持,分配专门寄存器存放栈地址,栈操作有专门的指令,效率比较高
  • 内存分配机制

    • 堆:系统有一个记录空闲内存链表,当系统申请堆空间时,会遍历该链表,寻找第一个空间大于申请空间的结点,删除该结点,并将该结点空间分配给程序
    • 栈:只要栈的剩余空间大于所申请空间,系统为程序提供内存,否则报异常提示栈溢出(栈是先进后出,后申请的变量一定会比先申请的变量先析构,所以内存可以使用连续的)

堆栈的分配方式

堆是动态分配的,由程序员自行管理,在C中是使用malloc和free,在C++中使用new和delete,关于具体如何使用以及原理可以参考同专栏的另一篇博客,在这里不详细叙述

在这一部分主要介绍栈的分配方式,栈有动态分配和静态分配两种方式,平时我们比较了解的是静态分配,关于栈的动态分配平时比较少涉及到,但是也需要了解一下

栈的静态分配是指变量的申请和释放都是由编译器进行管理的,但是动态分配的栈空间申请是由程序员申请的,由编译器进行释放

alloca函数

栈的动态申请是通过该函数实现的,这是一个在栈上申请内存空间的函数,在调用该函数的函数返回时其分配的内存就会自动释放

但是alloca不具有可移植性,在传统堆栈的机器上很难实现

void * __cdecl  alloca(size_t);

img

为什么可以在堆上动态申请内存还需要栈的动态申请呢

思考一下:在定义数组的时候指定数组的大小必须是一个常量,不能是变量,因为编译器需要知道应该为数组分配多大的内存空间,那么如果要使用变量就必须是动态申请内存空间,那么使用堆申请有什么问题呢

答案:堆的申请和释放的效率是比栈更低的,而且频繁的申请还会造成内存碎片,那么如果在一个数组生命周期很短,但是频繁申请和释放的场合中会导致程序的性能下降,造成内存碎片

那么在这种情况下就可以使用栈的动态申请,提高程序的效率