第34章内存分区模型
C++程序在运行时,内存会被划分为多个不同的区域,每个区域用于存储特定类型的数据,并具有不同的生命周期、管理方式等。不同编译器、操作系统细节略有差异,但核心分区模型统一。程序运行内存分为:代码区、全局区(静态区)、栈区、堆区,细分包含常量区(归属全局区)。
34.1 内存分区核心区分维度
各分区差异体现在三点:
- 存储内容:存放代码/全局变量/局部变量/动态内存等;
- 生命周期:程序全程/函数调用期间/手动分配释放;
- 管理主体:编译器自动管理 / 程序员手动new/delete。
34.2 代码区(Code Area)
34.2.1 特点
- 只读:防止程序运行时修改指令;
- 可共享:多个进程运行同一程序可共用一份代码,节省内存;
- 编译期确定大小,程序启动由操作系统加载进内存。
34.2.2 存储内容
所有函数二进制执行指令、编译器内置执行代码。 示例:
int add(int a,int b){ // add函数指令存储在代码区
return a+b;
}
34.3 全局区(静态区 Global Area)
34.3.1 特点
- 生命周期:程序启动分配,程序结束释放;
- 编译期分配空间;
- 未初始化全局/静态变量自动置0;
- 包含常量子区域(字符串常量、全局const常量,只读)。
34.3.2 存储内容
- 全局变量(所有函数外部定义);
- static修饰全局/局部静态变量;
- 字符串常量、全局const常量。 示例代码:
// 全局变量(全局区)
int g_a=10;
int g_b;// 未初始化,默认0
// 全局静态变量(全局区)
static int s_c=20;
// 全局常量(全局常量区)
const int c_d=30;
void func(){
// 局部静态变量,存全局区,非栈区
static int s_e = 200;
}
int main(){
// "test"字符串常量存储全局只读区
cout << "字符串常量地址:" << (void*)"test" << endl;
return 0;
}
34.4 栈区(Stack Area)
34.4.1 特点
- 自动管理:函数调用时分配,函数结束自动释放;
- 先进后出(FILO);
- 栈容量操作系统固定(通常几MB),超大局部数组、深层递归会栈溢出;
- 分配速度远快于堆。
34.2 存储内容
函数局部变量、函数形参、函数返回地址、局部临时对象。
注意:static局部变量不属于栈,存入全局区。 示例:
void print(int num){ // 参数num存栈区
int x=5; // 局部变量,栈区
string str="hello"; // str对象在栈,字符串数据在常量区
cout << num;
}
34.3 栈溢出常见场景
- 函数内定义超大局部数组:
int arr[1000000]; - 递归调用层数过深,持续占用栈帧:
void recursive(){
int buf[1000];
recursive(); // 无限递归,栈溢出崩溃
}
34.5 堆区(Heap Area)
34.5.1 特点
- 手动管理:C++使用new分配、delete释放;
- 生命周期自主:分配后直到手动释放,否则程序结束系统回收;
- 空间大(GB级别),分配释放存在内存碎片;
- 分配效率低于栈。
34.5.2 存储内容
new / new[] 动态分配的变量、数组、对象。
int main(){
int* p1=new int(10); // 单个int存堆
int* p2=new int[5]; // 数组存堆
delete p1;
delete[] p2;
return 0;
}
34.6 堆与栈对比表
| 对比维度 | 栈区 | 堆区 |
|---|---|---|
| 管理者 | 编译器自动 | 程序员手动new/delete |
| 生命周期 | 函数调用周期 | 分配到手动释放 |
| 空间大小 | 固定小容量 | 系统大内存 |
| 分配速度 | 极快 | 较慢 |
| 碎片问题 | 无 | 频繁分配释放产生碎片 |
| 溢出风险 | 深层递归、大数组栈溢出 | 仅耗尽系统内存才溢出 |
34.7 内存生命周期总览
- 代码区、全局区:程序启动 → 程序结束全程存在;
- 栈区:函数进入分配,函数执行完毕自动销毁;
- 堆区:new分配开始,delete释放结束。
34.8 常见内存错误与分区关联
- 野指针:堆内存释放后指针未置空,继续访问;
int* p=new int(10);
delete p;
*p=20; // 野指针,未定义行为
- 内存泄漏:堆内存分配后忘记释放;
void leak(){
int* p=new int(100);
// 无delete,内存永久占用
}
- 栈溢出:超大局部数组、无限递归;
- 修改只读常量:字符串常量在全局只读区,修改直接崩溃
char* str="abc";
str[0]='H'; // 非法修改只读内存,程序崩溃