C/C++ 内存管理完全指南
一篇帮你彻底搞懂 C/C++ 内存管理的复习笔记
一、C/C++ 内存分布
1.1 内存区域划分
┌─────────────────────────────────────┐ 高地址
│ 栈区 (Stack) │ ↓ 向下增长
│ 非静态局部变量/函数参数 │
├─────────────────────────────────────┤
│ 内存映射段 │
│ (共享库、进程间通信) │
├─────────────────────────────────────┤
│ 堆区 (Heap) │ ↑ 向上增长
│ 动态内存分配 │
├─────────────────────────────────────┤
│ 数据段(静态区) │
│ 全局变量/静态变量 │
├─────────────────────────────────────┤
│ 代码段(常量区) │
│ 可执行代码/只读常量 │
└─────────────────────────────────────┘ 低地址
1.2 经典例题
cpp
int globalVar = 1;
static int staticGlobalVar = 1;
void Test() {
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
1.3 变量存储位置速查表
| 变量 | 存储位置 | 说明 |
|---|---|---|
globalVar |
C. 数据段 | 全局变量 |
staticGlobalVar |
C. 数据段 | 静态全局变量 |
staticVar |
C. 数据段 | 静态局部变量 |
localVar |
A. 栈 | 局部变量 |
num1 |
A. 栈 | 局部数组 |
char2 |
A. 栈 | 局部数组 |
*char2 |
A. 栈 | 数组元素在栈上 |
pChar3 |
A. 栈 | 指针变量本身在栈上 |
*pChar3 |
D. 代码段 | 指向的字符串常量 |
ptr1 |
A. 栈 | 指针变量本身在栈上 |
*ptr1 |
B. 堆 | 指向的动态内存 |
二、C语言动态内存管理
2.1 三大函数对比
| 函数 | 功能 | 是否初始化 | 返回值 |
|---|---|---|---|
malloc |
分配指定字节数 | ❌ 不初始化 | void* |
calloc |
分配n个size字节 | ✅ 初始化为0 | void* |
realloc |
调整已分配内存 | 保留原数据 | void* |
2.2 使用示例
cpp
int* p1 = (int*)malloc(sizeof(int) * 4); // 分配16字节,不初始化
int* p2 = (int*)calloc(4, sizeof(int)); // 分配16字节,初始化为0
int* p3 = (int*)realloc(p2, sizeof(int) * 10); // 扩展到40字节
free(p1);
free(p3); // 注意:p2已被realloc,只需free(p3)
2.3 【面试题】malloc/calloc/realloc的区别
malloc:只分配空间,不初始化,内容随机
calloc:分配空间并初始化为0
realloc:调整已有内存大小,保留原数据
三、C++ 内存管理:new/delete
3.1 内置类型
cpp
void Test() {
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请3个int类型的空间
int* ptr6 = new int[3];
delete ptr4; // 释放单个
delete ptr5; // 释放单个
delete[] ptr6; // 释放数组,必须用 delete[]
}
⚠️ 注意 :
new和delete、new[]和delete[]必须匹配使用!
3.2 自定义类型
cpp
class A {
public:
A(int a = 0) : _a(a) {
cout << "A():" << this << endl;
}
~A() {
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main() {
// malloc/free:只开空间,不调用构造/析构
A* p1 = (A*)malloc(sizeof(A));
free(p1);
// new/delete:开空间 + 调用构造/析构
A* p2 = new A(1); // 调用构造函数
delete p2; // 调用析构函数
// 数组形式
A* p6 = new A[10]; // 调用10次构造函数
delete[] p6; // 调用10次析构函数
return 0;
}
3.3 核心区别
| 操作 | malloc/free | new/delete |
|---|---|---|
| 自定义类型 | 只开空间 | 开空间 + 构造/析构 |
| 内置类型 | 基本相同 | 基本相同 |
四、operator new 与 operator delete
4.1 本质
new和delete是操作符operator new和operator delete是全局函数new底层调用operator newdelete底层调用operator delete
4.2 实现原理
cpp
// operator new:底层就是 malloc
void* operator new(size_t size) {
void* p;
while ((p = malloc(size)) == 0) {
if (_callnewh(size) == 0) {
throw std::bad_alloc(); // 失败抛异常
}
}
return p;
}
// operator delete:底层就是 free
void operator delete(void* pUserData) {
if (pUserData == NULL) return;
_free_dbg(pUserData, _NORMAL_BLOCK);
}
4.3 关键流程图
new T
│
├──→ operator new (申请空间)
│ │
│ └──→ malloc
│ │
│ ├── 成功 → 返回指针
│ └── 失败 → 抛异常 bad_alloc
│
└──→ 调用构造函数
delete p
│
├──→ 调用析构函数
│
└──→ operator delete (释放空间)
│
└──→ free
五、new/delete 实现原理
5.1 内置类型
| 对比项 | new/delete | malloc/free |
|---|---|---|
| 单元素 | new / delete |
malloc / free |
| 连续空间 | new[] / delete[] |
malloc / free |
| 失败处理 | 抛异常 | 返回 NULL |
5.2 自定义类型
new 的原理:
1. 调用 operator new 申请空间
2. 在申请的空间上执行构造函数
delete 的原理:
1. 在空间上执行析构函数
2. 调用 operator delete 释放空间
new T[N] 的原理:
1. 调用 operator new[] → 内部调用 operator new 申请 N 个对象空间
2. 在申请的空间上执行 N 次构造函数
delete[] 的原理:
1. 在释放的对象空间上执行 N 次析构函数
2. 调用 operator delete[] → 内部调用 operator delete 释放空间
六、定位 new 表达式
6.1 概念
定位 new (placement-new) 用于在已分配的原始内存空间中调用构造函数初始化对象。
6.2 语法
cpp
new (place_address) type
new (place_address) type(initializer-list)
6.3 使用场景
配合内存池使用:内存池分配的内存未初始化,需用定位 new 调用构造函数。
6.4 示例
cpp
class A {
public:
A(int a = 0) : _a(a) { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
private:
int _a;
};
int main() {
// 方式一:malloc + 定位 new
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 显式调用构造函数
p1->~A(); // 显式调用析构函数
free(p1);
// 方式二:operator new + 定位 new
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10); // 带参数的构造函数
p2->~A();
operator delete(p2);
return 0;
}
七、malloc/free vs new/delete 对比
7.1 共同点
- 都从堆上申请空间
- 都需要手动释放
7.2 不同点速查表
| 区别点 | malloc/free | new/delete |
|---|---|---|
| 本质 | 函数 | 操作符 |
| 初始化 | 不初始化 | 可以初始化 |
| 空间大小 | 手动计算并传递 | 自动计算 |
| 返回值 | void* (需强转) |
对应类型指针 |
| 失败处理 | 返回 NULL (需判空) | 抛异常 (需捕获) |
| 自定义类型 | 只开空间 | 开空间 + 构造/析构 |
7.3 记忆口诀
malloc 是函数要强转,new 是操作符自动算;
malloc 失败返回空,new 失败抛异常;
自定义类型 new/delete,构造析构自动调!
八、一图总结
┌───────────────────────────────────────────────────────┐
│ C/C++ 内存管理总结 │
├───────────────────────────────────────────────────────┤
│ │
│ 【内存区域】 │
│ 栈 → 局部变量,向下增长 │
│ 堆 → 动态分配,向上增长 │
│ 数据段 → 全局/静态变量 │
│ 代码段 → 代码/常量 │
│ │
│ 【C语言方式】 │
│ malloc/calloc/realloc + free │
│ │
│ 【C++方式】 │
│ new/delete → 单个元素 │
│ new[]/delete[] → 连续空间 │
│ │
│ 【核心区别】 │
│ new/delete 会调用构造/析构函数 │
│ malloc/free 不会 │
│ │
│ 【定位 new】 │
│ 配合内存池使用,显式调用构造函数 │
│ │
└───────────────────────────────────────────────────────┘
希望这篇笔记能帮助你快速复习 C/C++ 内存管理知识!如有疑问,欢迎讨论交流。