C++内存分区模型

打印 上一主题 下一主题

主题 866|帖子 866|积分 2598

C++内存分区模型

在执行C++程序的过程中,内存大致分为四个区域:

  • 栈区(Stack):用于实现函数调用。由编译器自动分配释放,存放函数的参数值和局部变量等
  • 堆区(Heap):用于存放动态分配的变量。由程序员动态分配和释放,使用new和delete操作符
  • 全局/静态存储区(Data Segment & BSS Segment):存放全局变量和静态变量,程序结束时释放
    数据段 Data Segment (全局/静态存储区) : 存放初始化了的全局变量和静态变量
    BSS段 BSS Segment : 用于存放未初始化的全局变量和静态变量,节省空间,实际上不占用磁盘空间

  • 代码区(Text Segment):通常也被称为文本区或只读区。存放程序的二进制代码和常量,代码段是只读的,可以被多个进程共享
也有人认为 常量存储区 在内存中是独立的,C++标准并没有明确将常量存储区单独列为内存分区模型的一部分。因此,内存分区模型的确切细节可以根据不同的观点和上下文而有所不同
注意:不同的操作系统对程序内存的管理和划分会有所不同。上述的C++内存区域划分主要是针对通用的情况,并不限定在某个特定操作系统上
1. 代码区

代码区 (Code Segment) 也被称为文本段 (Text Segment) 或者只读区,主要包括可以执行的文件 ELF (Executable and Linkable Format) 和常量
代码区(Code Segment)也被称为文本段(Text Segment)或只读区,它有以下几个主要特征:

  • 代码区是程序的静态存储区域,存放程序执行所需的机器指令和常量数据,如字符串字面量和 const 变量的初始化值
  • 代码区的内容为只读属性,不允许修改程序中的代码,保障程序的安全性和稳定性
  • 程序运行前,代码区的内存大小已在可执行文件中确定,加载时系统会自动分配适当的内存
  • 多个进程可以共享相同的代码区,节省内存空间
  • 为提高效率,一些字符串字面量 (例如 "Hello") 可能会存放在共享的只读数据区而不是代码区
  • 若代码区中的常量初始化需要运行时计算,会将其放在数据区而不是代码区
  • 代码区也包含只读数据,如跳转表和常量表等
  • 程序运行时,代码区通常不会改变大小,若需扩展,依赖操作系统提供的机制
2. 全局/静态存储区

全局/静态存储区主要包括以下两个部分:

  • 数据段(Initialized Data Segment)

    • 用于存放初始化了的全局变量和静态变量
    • 存储在此段的数据在程序运行前分配,运行结束后释放
    • 有初始化值的全局变量和静态变量存放在此
    • 数据段属于可读可写区域

  • BSS段(Block Started by Symbol)

    • 用于存放未初始化的全局变量和静态变量
    • 不占用实际的磁盘空间,只在编译时预留内存空间
    • 无初始化值的全局变量和静态变量存放在此
    • 程序启动时会自动初始化为默认值
    • 属于可读写区域

两者的主要区别在于初始化状况。全局变量和静态变量可以显式初始化,如果没有显式初始化,它们会被自动初始化为默认值(0 或 nullptr,取决于变量的类型),该区域的数据在程序整个运行周期中一直存在
3. 栈区

栈区是用于实现函数调用和局部变量存储的一种内存区域。在 C++ 中,每当调用一个函数时,系统会自动在栈区为该函数分配一块内存,称为栈帧(Stack Frame)。栈帧用于存储函数的参数值、局部变量以及函数执行期间的一些控制信息 ^f6a053
以下是栈区的一些关键特点:

  • LIFO(Last-In-First-Out)原则:栈区采用后进先出的原则,即最后压入栈的数据会最先弹出。这是因为每次调用函数时,会将函数的栈帧压入栈顶,函数执行结束后,栈帧会从栈顶弹出
  • 自动分配和释放:栈区的内存分配和释放是由编译器自动管理的,当进入函数时,会为该函数分配一块连续的内存区域,并在函数返回时自动释放这块内存。这样的自动分配和释放使得栈区的内存管理相对高效,但也意味着栈上的数据生命周期必须在函数调用内部
  • 局部变量存储:栈区主要用于存储函数的局部变量,这些变量的生命周期与函数的调用和返回相对应。当函数调用结束,栈帧会被销毁,其中的局部变量也会被销毁,因此在函数外部无法访问这些局部变量
  • 函数调用:当调用函数时,函数的参数值和返回地址会被压入栈帧中,函数执行过程中的其他局部变量也会存储在栈帧中。函数返回时,栈帧会从栈顶弹出,恢复调用函数的现场
  • 栈溢出:栈区的大小是有限的,如果递归调用过深或者函数中使用了大量的局部变量,可能导致栈溢出(Stack Overflow)错误,即栈区的内存已被耗尽
  • 线程私有:每个线程都有自己的栈区,栈区的内存是线程私有的,不同线程之间的栈区不共享
3.1 栈溢出

栈溢出(Stack Overflow)是指程序在运行过程中,不断调用函数,导致栈空间被占满,无法再分配新的栈帧。栈是一种先进后出的数据结构,用于存储局部变量、参数、返回地址等信息。栈的大小是有限的,一般为8~10MB。栈溢出通常发生在递归调用过深或死循环的情况下
以下是一个程序在栈区的示例图。在主函数调用自定义的函数,函数内部形成递归

当程序一直运行时,递归的函数会不断占用栈区的内存空间,从而导致 栈溢出

3.2 缓冲区溢出

在C++中,缓冲区溢出通常发生在栈区。栈区用于存储函数调用时的局部变量和函数调用的返回地址
当函数调用时,函数的栈帧(包含局部变量和其他控制信息)被压入栈中。如果函数中使用了缓冲区(数组)并且没有进行足够的边界检查,很容易导致写入超出缓冲区边界的数据,从而覆盖栈上其他变量和控制信息,这就是缓冲区溢出
例如以下代码就是一个缓冲区溢出的案例:
[code]#include int main(){    //创建并初始化数组    char arr[3] = {'a', 'a', 'a'};    //向数组写入数据,但超出了数组的边界    for (int i = 0; i

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表