🧠 C/C++ 内存管理 ------ 程序的"五脏六腑"在哪里?
💡 你写的每一行代码,背后都有一块内存默默支撑。
学会内存管理,就像学会给程序"体检"和"做手术"!
本章带你搞懂:
✅ 程序运行时,变量到底住在哪里?
✅
malloc/free和new/delete有什么区别?✅ 为什么 C++ 要发明
new?✅ 什么是"定位 new"?它有什么用?
1️⃣ C/C++ 的内存分布 ------ 程序的"五大区域"
想象你的程序是一个房子,内存就是它的房间布局:
| 区域 | 别名 | 存什么? | 特点 |
|---|---|---|---|
| 栈(Stack) | 堆栈 | 局部变量、函数参数、返回值 | 自动分配/释放,向下增长,速度快 |
| 堆(Heap) | 动态区 | malloc / new 申请的空间 |
手动管理,向上增长,灵活但易出错 |
| 数据段(Data Segment) | 静态区 | 全局变量、静态变量 | 程序启动时初始化,结束时释放 |
| 代码段(Text Segment) | 常量区 | 函数代码、字符串常量(如 "abcd") |
只读,不可修改 |
| 内存映射段 | - | 动态库、共享内存等 | 高级用法,暂时了解即可 |
🧩 小测试:下面这些变量住在哪?
c++
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); // 指针在栈,指向堆
}
✅ 答案速查表:
| 变量 | 位置 | 解释 |
|---|---|---|
globalVar |
C. 数据段 | 全局变量 |
staticGlobalVar |
C. 数据段 | 静态全局变量 |
staticVar |
C. 数据段 | 静态局部变量(生命周期=整个程序) |
localVar |
A. 栈 | 普通局部变量 |
num1 |
A. 栈 | 局部数组(自动分配) |
char2 |
A. 栈 | 数组本身在栈 |
*char2(即 'a') |
A. 栈 | 数组内容也在栈 |
pChar3(指针) |
A. 栈 | 指针变量是局部变量 |
*pChar3(即 "abcd" 的首字符) |
D. 代码段 | 字符串字面量在常量区 |
ptr1(指针) |
A. 栈 | 指针变量在栈 |
*ptr1(动态内存) |
B. 堆 | malloc 分配的空间在堆 |
🔍 小技巧:
- 变量本身在哪?看定义位置(局部→栈,全局/静态→数据段)
- 变量指向的内容在哪?看怎么分配的 (
"..."→ 代码段,malloc/new→ 堆)
2️⃣ C 语言的动态内存管理:malloc/calloc/realloc/free
📌 四兄弟介绍:
| 函数 | 作用 | 是否初始化 | 返回值 |
|---|---|---|---|
malloc(size) |
申请 size 字节 |
❌ 不初始化(垃圾值) | void* |
calloc(n, size) |
申请 n 个 size 字节 |
✅ 全部初始化为 0 | void* |
realloc(ptr, new_size) |
调整已有内存大小 | 保留原数据 | void*(可能新地址!) |
free(ptr) |
释放动态内存 | - | 无返回值 |
✅ 示例:三兄弟怎么用?
c++
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
// malloc:不初始化
int* p1 = (int*)malloc(4 * sizeof(int));
// calloc:初始化为0
int* p2 = (int*)calloc(4, sizeof(int)); // [0,0,0,0]
// realloc:扩容(注意:可能移动地址!)
int* p3 = (int*)realloc(p2, 10 * sizeof(int)); // 原数据保留
// 使用...
for (int i = 0; i < 10; ++i) cout << p3[i] << " "; // 前4个是0,后6个未定义
// 释放
free(p1);
free(p3); // 注意:p2 已被 realloc "吞掉",不能再 free(p2)!
return 0;
}
⚠️ 重要面试题 :
realloc(p, new_size)后,还能free(p)吗?❌ 不能! 如果
realloc移动了内存,p已失效,free(p)会导致崩溃!
3️⃣ C++ 的内存管理:new 和 delete
C++ 觉得 malloc/free 太原始,于是推出了更智能的 new/delete!
✅ 用于内置类型(int、double 等)
c++
// 单个
int* p1 = new int; // 未初始化(随机值)
int* p2 = new int(10); // 初始化为10
// 数组
int* p3 = new int[5]; // 5个未初始化的int
int* p4 = new int[5](); // 5个初始化为0的int(C++11)
// 释放
delete p1;
delete p2;
delete[] p3; // 数组必须用 delete[]
delete[] p4;
📌 口诀:
new→deletenew[]→delete[]
千万别混用!否则未定义行为!
✅ 用于自定义类型(类对象)
这才是 new/delete 的真正优势!
c++
class A {
public:
A(int a = 0) : _a(a) {
cout << "构造 A(" << _a << ") @ " << this << endl;
}
~A() {
cout << "析构 A(" << _a << ") @ " << this << endl;
}
private:
int _a;
};
int main() {
// malloc:只分配空间,不调构造!
A* p1 = (A*)malloc(sizeof(A)); // 对象未初始化!危险!
// new:分配空间 + 调用构造函数!
A* p2 = new A(10); // 自动调用 A(10)
// 释放
free(p1); // 不调析构!资源可能泄漏!
delete p2; // 先调析构,再释放内存!
// 数组
A* p3 = new A[3]; // 调用3次默认构造
delete[] p3; // 调用3次析构 + 释放
return 0;
}
💡 核心区别 :
new/delete会自动调用构造/析构函数 ,malloc/free不会!
4️⃣ operator new 和 operator delete ------ new 的底层实现
你以为 new 是魔法?其实它也是"站在巨人肩膀上"!
new表达式 = 调用operator new分配内存 + 调用构造函数delete表达式 = 调用析构函数 + 调用operator delete释放内存
🧱 底层实现(简化版):
c++
// operator new 实际调用 malloc
void* operator new(size_t size) {
void* p = malloc(size);
if (!p) throw std::bad_alloc(); // 失败抛异常
return p;
}
// operator delete 实际调用 free
void operator delete(void* p) {
free(p);
}
✅ 所以:
new失败 → 抛异常(不是返回 NULL)malloc失败 → 返回 NULL
5️⃣ new 和 delete 的实现原理
🔹 内置类型:
new int≈malloc(sizeof(int))(但失败抛异常)delete p≈free(p)
🔹 自定义类型:
new A(10) 做了两件事:
- 调用
operator new(sizeof(A))申请内存 - 在该内存上调用
A(10)构造函数
delete p 做了两件事:
- 调用
p->~A()析构函数 - 调用
operator delete(p)释放内存
new A[3]:
- 调用
operator new[](3 * sizeof(A)) - 调用 3 次构造函数
delete[] p:
- 调用 3 次析构函数
- 调用
operator delete[]
6️⃣ 定位 new(Placement new)------ 在指定内存上构造对象
💡 场景:你已经有一块内存(比如内存池分配的),想在这块内存上"激活"一个对象。
✅ 语法:
c++
new (地址) 类型(参数);
✅ 示例:
c++
class A {
public:
A(int a) : _a(a) { cout << "构造 A(" << a << ")" << endl; }
~A() { cout << "析构 A(" << _a << ")" << endl; }
private:
int _a;
};
int main() {
// 1. 先分配原始内存(不调构造!)
void* raw = malloc(sizeof(A));
// 2. 在 raw 上构造 A 对象
A* p = new (raw) A(42); // 定位 new
// 3. 使用对象
// ...
// 4. 手动调析构(因为内存不是 new 分配的!)
p->~A();
// 5. 释放原始内存
free(raw);
return 0;
}
🌟 用途 :高性能场景(如游戏引擎、数据库)中的内存池技术!
7️⃣ malloc/free vs new/delete ------ 终极对比表
| 对比项 | malloc/free |
new/delete |
|---|---|---|
| 类型 | C 函数 | C++ 操作符 |
| 初始化 | ❌ 不初始化 | ✅ 可初始化(new int(10)) |
| 类型安全 | ❌ 返回 void*,需强转 |
✅ 返回具体类型指针 |
| 失败处理 | 返回 NULL |
抛出 std::bad_alloc 异常 |
| 自定义类型 | ❌ 只分配内存 | ✅ 分配 + 调用构造/析构 |
| 数组支持 | 需手动计算大小 | new T[N] 自动计算 |
| 可重载 | ❌ 不可重载 | ✅ 可重载 operator new/delete |
✅ 总结:
- C 项目 → 用
malloc/free- C++ 项目 → 优先用
new/delete,尤其是涉及类对象时!
🧠 本章口诀总结
🏠 内存五区记心间 :
栈存局部快如电,堆上动态要管严,
全局静态数据段,代码常量只读安。
🔧 malloc 原始 new 智能 :
malloc 只管分内存,new 还会调构造;
free 不理析构事,delete 清理全包办!
⚠️ 匹配使用是铁律 :
new 配 delete,new[] 配 delete[],
混用等于埋炸弹!
🎯 定位 new 有妙用 :
内存池中显身手,手动构造析构走!
🌟 动手建议 :
把文中的代码全部敲一遍,观察输出,你会对内存有"肌肉记忆"!