C++ 动态内存
一、程序内存分区概述
C++ 程序运行时内存主要分为:栈、堆、全局/静态区、常量区、代码区 。
日常开发中动态内存管理核心围绕**栈(Stack)与堆(Heap)**展开。
二、栈(Stack)详细说明
-
管理方式
由操作系统自动分配、自动回收,无需程序员手动干预。
-
存储内容
函数局部变量、函数形参、临时变量、局部实例化对象。
-
生命周期
绑定函数作用域,函数执行结束、作用域销毁时,栈内存立刻自动释放。
-
空间特性
- 空间容量小、上限固定(系统默认数MB);
- 内存地址连续,地址由高向低增长;
- 空间不足会触发栈溢出(Stack Overflow)。
-
访问效率
内存连续、CPU寻址高效,读写速度快。
-
线程属性
每个线程拥有独立栈空间,线程私有,不存在多线程竞争问题。
三、堆(Heap)详细说明
-
管理方式
由程序员手动控制,通过
new / new[]申请,delete / delete[]手动释放。 -
存储内容
动态变量、动态数组、大型对象、需要跨作用域长期使用的数据。
-
生命周期
不受函数作用域限制,内存分配后会一直常驻,直到手动释放或程序进程结束;
未手动释放会造成内存泄漏。
-
空间特性
- 空间极大,受系统虚拟内存限制,可灵活分配大容量内存;
- 内存地址不连续,频繁分配释放易产生内存碎片;
- 分配失败返回空指针或抛出异常,无溢出风险。
-
访问效率
分配与释放开销大,碎片化内存导致访问效率低于栈。
-
线程属性
全局共享内存,多线程同时操作堆空间需要加锁保证线程安全。
四、堆与栈核心区别对照表
| 对比维度 | 栈(Stack) | 堆(Heap) |
|---|---|---|
| 内存管理者 | 系统自动管理 | 程序员手动管理 |
| 分配方式 | 自动分配 | new 动态手动分配 |
| 释放方式 | 作用域结束自动释放 | 必须手动 delete 释放 |
| 空间大小 | 容量小、固定上限 | 空间大、支持动态扩容 |
| 内存布局 | 连续内存空间 | 非连续内存,易产生碎片 |
| 分配速度 | 快速高效 | 分配释放较慢 |
| 生命周期 | 随函数/作用域销毁 | 生命周期自定义,跨函数可用 |
| 典型用途 | 局部小变量、临时数据 | 大容量数据、动态数组、长生命周期对象 |
| 溢出问题 | 易发生栈溢出 | 无溢出,仅分配失败 |
| 地址增长方向 | 高地址 → 低地址 | 低地址 → 高地址 |
| 线程归属 | 线程私有 | 进程全局共享 |
五、C++ 动态内存:new 与 delete
5.1 核心作用
new:在堆区手动分配内存,自动调用类的构造函数;delete:释放堆区内存,自动调用类的析构函数。
5.2 单个内存分配与释放
cpp
#include <iostream>
using namespace std;
int main()
{
// 堆上分配单个 double 内存
double* pvalue = new double;
*pvalue = 29494.99;
cout << "堆内存变量值:" << *pvalue << endl;
// 释放堆内存
delete pvalue;
pvalue = nullptr; // 释放后置空,避免野指针
return 0;
}
5.3 内存分配失败判断
堆内存不足时 new 会分配失败,增加空指针判断保证程序健壮性:
cpp
double* p = nullptr;
if (!(p = new double))
{
cout << "内存不足,分配失败!" << endl;
exit(1);
}
六、动态数组内存分配
6.1 一维动态数组
cpp
// 分配堆数组
int* arr = new int[10];
// 使用数组
for(int i = 0; i < 10; ++i)
arr[i] = i;
// 数组释放必须使用 delete[]
delete[] arr;
arr = nullptr;
6.2 二维动态数组
cpp
// 二维数组开辟
int row = 3, col = 4;
int** mat = new int*[row];
for(int i = 0; i < row; ++i)
{
mat[i] = new int[col];
}
// 二维数组释放
for(int i = 0; i < row; ++i)
{
delete[] mat[i];
}
delete[] mat;
mat = nullptr;
6.3 关键规则
new[]开辟数组,必须配套delete[]释放;- 混用
new / delete[]会导致内存泄漏、程序崩溃。
七、对象动态内存分配
使用 new / delete 操作类对象,会自动触发构造与析构:
cpp
#include <iostream>
using namespace std;
class Box
{
public:
Box()
{
cout << "调用构造函数" << endl;
}
~Box()
{
cout << "调用析构函数" << endl;
}
};
int main()
{
// 堆上创建 4 个对象,调用 4 次构造
Box* boxArr = new Box[4];
// 释放数组对象,调用 4 次析构
delete[] boxArr;
boxArr = nullptr;
return 0;
}
八、new/delete 与 malloc/free 对比
malloc / free
- C 语言标准库函数;
- 仅负责原始内存的申请与释放;
- 不调用构造函数、析构函数,不适合 C++ 面向对象开发。
new / delete
- C++ 专属运算符;
- 分配内存 + 自动调用构造函数;
- 释放内存 + 自动调用析构函数;
- 类型安全,支持重载,C++ 项目优先使用。
九、动态内存使用规范与注意事项
- 配对原则:
new对应delete,new[]对应delete[],禁止混用; - 内存泄漏:堆内存必须手动释放,长期遗漏会导致程序内存占用持续上涨;
- 野指针规避:内存释放后,立即将指针赋值为
nullptr; - 禁止重复释放、悬空指针访问已释放内存;
- 场景选择:
- 短期、局部、小数据 → 使用栈内存;
- 大数据、动态长度、跨作用域 → 使用堆内存;
- 多线程开发中,共享堆资源需做好同步加锁。
十、代码示例:栈内存 vs 堆内存
cpp
// 栈内存:自动分配自动释放
void stackDemo()
{
int a = 10; // 栈变量
char buf[1024]; // 栈数组
Box box; // 栈对象
}
// 函数结束,所有栈变量自动销毁
// 堆内存:手动管理
void heapDemo()
{
int* pNum = new int(100);
Box* pBox = new Box;
int* pArr = new int[20];
// 手动释放
delete pNum;
delete pBox;
delete[] pArr;
}