内存被分成五个区:栈、堆、静态存储区、常量区、代码区。
C++ 的内存管理是其最强大但也最复杂的特性之一。它赋予了程序员对硬件的极致控制权,但也要求程序员承担起精确管理的责任。
现代 C++ 的内存管理哲学已经从手动管理 转向了自动化和智能化 ,核心思想是 RAII。
C++ 内存五大分区详解
我们可以把程序的内存想象成一栋大楼,不同的区域住着不同的"住户":
1. 栈区
- 住户:局部变量、函数参数、返回值。
- 特点 :
- 自动管理:由编译器自动分配和释放,无需人工干预。
- 速度快:分配方式类似于数据结构中的"栈"(先进后出),只是移动栈顶指针,效率极高。
- 空间小 :大小有限(通常由操作系统限制,如 2MB),如果分配过大(如超大数组)或递归过深,会导致栈溢出。
- 注意:不要返回局部变量的地址,因为函数结束时,该内存会被立即回收。
2. 堆区
- 住户 :动态分配的内存(
new/malloc出来的对象)。 - 特点 :
- 手动管理 :必须由程序员手动分配(
new)和释放(delete)。如果忘了释放,就会产生内存泄漏。 - 空间大:受限于计算机系统的虚拟内存,空间非常充裕。
- 速度较慢 :分配时需要寻找合适的空闲内存块,容易产生内存碎片。
- 手动管理 :必须由程序员手动分配(
- 现代 C++ 建议 :尽量使用智能指针(如
std::unique_ptr,std::shared_ptr)来自动管理堆内存,避免手动delete。
3. 静态存储区
- 住户 :全局变量、静态变量(
static)。 - 细分 :
- 已初始化数据段 (.data) :存放已初始化的全局/静态变量(如
int a = 10;)。 - 未初始化数据段 (.bss) :存放未初始化或初始化为 0 的全局/静态变量(如
int b;,系统会自动置零)。
- 已初始化数据段 (.data) :存放已初始化的全局/静态变量(如
- 特点:生命周期贯穿整个程序运行期间,程序启动时分配,结束时由系统释放。
4. 常量区
- 住户 :常量数据,如字符串字面量(
"Hello")、const修饰的全局常量。 - 特点 :
- 只读:这部分内存是只读的,试图修改会导致程序崩溃(段错误)。
- 共享:相同的字符串常量在内存中通常只存一份。
- 注意 :
const修饰的局部 变量通常存放在栈区,而不是常量区。
5. 代码区
- 住户:编译后的二进制机器指令(函数体代码)。
- 特点 :
- 只读:防止程序意外修改自身的指令。
- 共享:如果同一个程序运行多次,操作系统可能让这些实例共享同一份代码区。
核心对比:栈 vs 堆
| 对比项 | 栈 | 堆 |
|---|---|---|
| 管理方式 | 编译器自动管理 | 程序员手动控制 (new/delete) |
| 分配效率 | 极高(指针移动) | 较低(需查找空闲链表,易碎片化) |
| 空间大小 | 较小(容易溢出) | 很大(受限于虚拟内存) |
| 生长方向 | 向低地址增长(向下) | 向高地址增长(向上) |
| 生命周期 | 随函数作用域结束而释放 | 从分配直到手动释放 |
代码演示
cpp
#include <iostream>
#include <cstring>
// 1. 全局变量 -> 静态存储区 (.data)
int g_var = 10;
// 2. 未初始化全局变量 -> 静态存储区 (.bss)
int g_uninit_var;
// 3. 常量 -> 常量区
const char* str_const = "Hello Memory";
void testFunc(int param) { // param -> 栈区
// 4. 局部变量 -> 栈区
int local_var = 20;
// 5. 静态局部变量 -> 静态存储区 (.data)
static int s_var = 30;
// 6. 动态分配 -> 堆区
int* heap_ptr = new int(40);
std::cout << "=== 内存地址演示 ===" << std::endl;
std::cout << "全局变量 (静态区): " << &g_var << std::endl;
std::cout << "静态局部 (静态区): " << &s_var << std::endl;
std::cout << "字符串常量 (常量区): " << (void*)str_const << std::endl; // 打印字符串内容的地址
std::cout << "函数参数 (栈区): " << ¶m << std::endl;
std::cout << "局部变量 (栈区): " << &local_var << std::endl;
std::cout << "堆内存 (堆区): " << heap_ptr << std::endl;
std::cout << "函数代码 (代码区): " << (void*)testFunc << std::endl;
delete heap_ptr; // 记得释放堆内存
}
int main() {
testFunc(100);
return 0;
}