C++ 程序运行时,内存通常被划分为以下几个主要区域:
1. 栈区(Stack)
特点:
-
由编译器自动分配和释放
-
存储局部变量、函数参数、返回地址等
-
先进后出(FILO)的数据结构
-
内存空间有限(通常几MB)
-
分配速度快
示例:
void func() {
int a = 10; // a在栈上
double b = 3.14; // b在栈上
char c = 'A'; // c在栈上
} // 函数结束,a,b,c自动释放
2. 堆区(Heap)/ 自由存储区(Free Store)
特点:
-
程序员手动分配和释放(C:malloc/free,C++:new/delete)
-
内存空间大,只受系统虚拟内存限制
-
分配速度较慢
-
需要防止内存泄漏
示例:
int* p1 = new int(10); // 在堆上分配一个int
int* arr = new int[100]; // 在堆上分配数组
double* p2 = new double(3.14); // 在堆上分配double
delete p1; // 手动释放
delete[] arr; // 释放数组
3. 全局/静态存储区
包含:
A. 数据段(Data Segment)
-
.data段:已初始化的全局变量和静态变量
-
.bss段:未初始化或初始化为0的全局变量和静态变量(程序启动时自动清零)
B. 静态存储区
- 存储static变量(包括全局static和局部static)
特点:
-
在程序编译时分配
-
生命周期贯穿整个程序
-
程序结束时由系统释放
示例:
int global_var = 100; // .data段(已初始化全局变量)
int global_uninit; // .bss段(未初始化全局变量)
static int static_global = 50; // .data段(静态全局变量)
void func() {
static int static_local = 30; // 静态局部变量,在.data段
// 只在第一次进入函数时初始化,保持值不变
}
4. 常量存储区
特点:
-
存储字符串常量、const修饰的全局常量
-
只读,不可修改
-
程序结束时由系统释放
示例:
const int MAX_SIZE = 100; // 可能存储在常量区(优化可能放入代码段)
const char* str = "Hello"; // "Hello"在常量区,str指针在栈或数据段
// 尝试修改会报错或导致未定义行为
// str[0] = 'h'; // 错误!常量区不可修改
5. 代码区(Text Segment)
特点:
-
存储程序的可执行代码(机器指令)
-
只读,防止程序意外修改自身指令
-
在内存中通常有固定位置
6. C++11 新增:线程局部存储区
特点:
-
每个线程有独立的存储空间
-
使用
thread_local关键字声明
示例:
thread_local int thread_var = 0; // 每个线程都有自己的副本
内存布局图示
高地址
┌─────────────────┐
│ 栈区 │ ← 向下生长
├─────────────────┤
│ ... │
├─────────────────┤
│ 堆区 │ ← 向上生长
├─────────────────┤
│ .bss段 │ 未初始化全局/静态变量
├─────────────────┤
│ .data段 │ 已初始化全局/静态变量
├─────────────────┤
│ 常量区 │ 字符串常量等
├─────────────────┤
│ 代码区 │ 程序指令
└─────────────────┘
低地址
代码示例展示不同区域
#include <iostream>
using namespace std;
// 全局变量 - 数据段
int global_init = 100; // .data段
int global_uninit; // .bss段
// 常量 - 常量区
const int MAX = 1000; // 常量区
const char* STR = "Hello"; // "Hello"在常量区,STR指针在.data段
void testMemory() {
// 局部变量 - 栈区
int local = 10; // 栈区
static int static_local = 20; // .data段(静态局部变量)
// 动态分配 - 堆区
int* heap_var = new int(30); // 堆区
// 线程局部存储
thread_local int tls_var = 40; // 线程局部存储区
cout << "栈变量地址: " << &local << endl;
cout << "静态局部变量地址: " << &static_local << endl;
cout << "堆变量地址: " << heap_var << endl;
cout << "全局变量地址: " << &global_init << endl;
cout << "常量地址: " << &MAX << endl;
cout << "字符串常量地址: " << (void*)STR << endl;
delete heap_var;
}
int main() {
testMemory();
return 0;
}
各区域比较表
| 区域 | 分配/释放 | 生命周期 | 大小限制 | 访问速度 | 存储内容 |
|---|---|---|---|---|---|
| 栈区 | 自动 | 函数作用域 | 较小(几MB) | 最快 | 局部变量、参数 |
| 堆区 | 手动 | 直到delete | 大(受系统限制) | 慢 | 动态分配对象 |
| 数据段 | 编译时 | 程序运行期 | 中等 | 快 | 全局/静态变量 |
| 常量区 | 编译时 | 程序运行期 | 中等 | 快 | 常量、字符串字面量 |
| 代码区 | 编译时 | 程序运行期 | 中等 | 只读 | 程序指令 |
注意事项
-
栈溢出:递归过深或局部变量过大
void recursive(int depth) { int arr[1000]; // 大量栈分配 recursive(depth + 1); // 可能栈溢出 } -
内存泄漏:堆内存未释放
void leak() { int* p = new int[100]; // 分配 // 忘记 delete[] p; } -
悬空指针:访问已释放内存
int* p = new int(10); delete p; *p = 20; // 错误!p是悬空指针 -
多线程安全:
-
栈变量:每个线程独立,安全
-
全局/静态变量:需要同步机制
-
堆变量:需谨慎管理所有权
-