核心要点速览

  • 五大分区:栈(临时空间,自动管理)、堆(动态空间,手动管理)、全局 / 静态区(持久空间)、常量存储区(只读)、代码区(指令存储)
  • 关键点:各分区存储内容 / 生命周期 / 分配方式、栈与堆的区别、const 变量存储位置、线程局部存储特性
  • 补充:全局 / 局部 / 静态局部 / 静态全局变量的存储、生命周期及作用域差异

一、五大内存分区

1. 栈(stack):函数调用的临时空间

  • 存储内容:函数内局部变量(非 static)、函数参数、返回地址(调用后需返回的下一条指令地址)、寄存器上下文(函数调用时保存的 CPU 寄存器状态)
  • 生命周期:随函数调用创建,函数执行结束后自动释放(无需手动干预)
  • 特点
    • 分配效率:编译器通过移动栈顶指针(SP 寄存器)完成分配 / 释放,效率极高
    • 内存特性:地址连续(栈帧结构),生长方向向下(从高地址向低地址扩展)
    • 大小限制:默认固定(通常 1-8MB,可通过编译器 / 操作系统配置),超出则触发栈溢出
    • 初始化:未初始化的局部变量值为 “垃圾值”(随机值,取决于内存历史数据)

2. 堆(heap):动态分配的手动管理空间

  • 存储内容:通过new/malloc/new[]/calloc等动态分配的内存
  • 生命周期:从手动分配开始,到delete/free/delete[]手动释放结束;若未释放,仅在程序终止后由操作系统回收(运行时不会自动释放)
  • 特点
    • 分配效率:需通过内存分配算法查找空闲块,效率低于栈
    • 内存特性:地址不连续(受空闲块分布影响),生长方向向上(从低地址向高地址扩展,与栈相反)
    • 大小灵活性:理论上可接近系统可用内存(GB 级),适合存储大尺寸 / 生命周期不确定的数据
    • 潜在问题:
      • 内存碎片:频繁分配 / 释放不同大小的块,导致空闲块分散,无法满足大内存分配需求
      • 内存泄漏:未释放已不再使用的堆内存,导致可用内存逐渐减少
      • 野指针:释放后未置空的指针,继续访问会触发未定义行为(崩溃、数据篡改)
      • 重复释放:对同一指针多次调用delete/free,导致堆结构损坏

3. 全局 / 静态存储区

  • 存储内容
    • 全局变量(定义在函数外部的变量)
    • 静态变量:
      • 全局静态变量(定义在函数外)
      • 局部静态变量(定义在函数内)
  • 生命周期:从程序加载(main函数执行前)到程序终止(main函数执行后),由操作系统自动分配和释放
  • 特点
    • 分配时机:编译期确定大小,程序启动时由操作系统一次性分配
    • 初始化:未显式初始化的变量会被自动置 0(区别于栈的 “垃圾值”),显式初始化则在程序启动前完成(早于main
    • 作用域差异:
      • 全局变量:作用域为整个程序(其他文件可通过extern声明访问)
      • 全局静态变量:作用域仅限当前文件(避免跨文件命名冲突)
      • 局部静态变量:作用域仅限定义它的函数内部(但生命周期为程序全程)
    • 初始化特性:局部静态变量仅在第一次进入函数时初始化,后续调用不再重复初始化

4. 常量存储区

  • 存储内容
    • 字符串常量
    • 全局const常量(定义在函数外)
  • 生命周期:程序全程(从加载到终止)
  • 特点
    • 只读属性:尝试修改会触发未定义行为(通常为程序崩溃,因内存被标记为只读)
    • 优化共享:相同字符串常量可能被编译器合并(如const char* p1 = "abc"; const char* p2 = "abc";p1p2指向同一地址,节省内存)
    • 易混淆:
      • 局部const变量(如函数内的const变量)存储在上(仅限制修改,生命周期同局部变量,本质是栈内存)
      • 数组初始化拷贝:char arr[] = "hello";中,"hello"在常量区,但数组arr在栈上(编译器会将常量区的字符串拷贝到栈数组中,修改arr元素是合法的)

5. 代码区

  • 存储内容:程序的机器指令(二进制代码)、只读数据(如常量表达式计算结果)
  • 生命周期:程序全程(从加载到终止)
  • 特点
    • 只读保护:操作系统会将代码区标记为只读,防止意外修改指令(如缓冲区溢出攻击)
    • 共享性:多进程 / 线程运行同一程序时,共享同一份代码区(仅数据区独立),大幅节省内存
    • 加载方式:程序启动时,由操作系统从磁盘可执行文件(如.exe/.out)加载到内存,与数据区分离存储

二、对比表

分区 存储内容 生命周期 分配方式 管理方式
局部变量、参数、返回地址 函数调用期间 编译器自动 自动释放
动态分配内存(new/malloc) 手动分配→手动释放 程序员手动 手动释放(delete/free)
全局 / 静态存储区 全局变量、静态变量 程序全程 编译期分配 程序结束自动释放
常量存储区 字符串常量、全局 const 常量 程序全程 编译期分配 程序结束自动释放
代码区 机器指令、只读数据 程序全程 程序加载时 只读不修改

三、线程局部存储(thread_local)

  • 存储内容:由thread_local修饰的变量(线程私有变量,C++11 引入)
  • 生命周期:与线程绑定(线程创建时初始化,线程结束时自动释放)
  • 特点
    • 线程隔离性:每个线程拥有独立副本,修改当前线程的变量不影响其他线程(解决多线程全局变量竞争问题)
    • 作用域:同普通变量(可定义为全局或局部),但仅对当前线程可见
    • 初始化:若为局部thread_local变量,每个线程第一次进入作用域时初始化一次
    • 示例:
1
2
thread_local int t_val = 0; // 每个线程有独立的t_val
void func() { t_val++; } // 线程1调用后t_val=1,线程2调用后t_val=1(互不影响)

四、问答

1. 全局 / 局部 / 静态变量的区别?

变量类型 存储分区 生命周期 作用域
全局变量 全局 / 静态存储区 程序全程 整个程序(跨文件可访问)
静态全局变量 全局 / 静态存储区 程序全程 仅限当前文件
局部变量 函数调用期间 仅限函数内部
静态局部变量 全局 / 静态存储区 程序全程 仅限函数内部

2. const 变量一定在常量区吗?

不一定:

  • 全局 const:常量区(只读,程序全程);
  • 局部 const:栈上(仅限制修改,生命周期同局部变量);
  • static const:全局 / 静态存储区(程序全程)。

3. 栈和堆的差异?

  • 管理:栈自动、堆手动;
  • 效率:栈 > 堆;
  • 连续性:栈连续、堆不连续;
  • 生命周期:栈随函数、堆随手动释放;
  • 大小:栈小且固定、堆大且灵活。