C++: 动态内存分配
最后更新:2026-08-26
前面的课程里,变量和数组都在栈(Stack)上分配——大小固定,函数结束就释放。
但如果你要存不确定数量的数据(比如用户输入 1000 个整数,但编译时不知道),就需要动态内存分配——在堆(Heap)上分配内存。
1. 栈 vs 堆
(1) 1.1 两种内存区域
| 对比 | 栈(Stack) | 堆(Heap) |
|---|---|---|
| 分配时机 | 编译时确定 | 运行时确定 |
| 大小 | 固定(编译时确定) | 不固定(运行时申请) |
| 释放时机 | 自动释放(函数结束) | 手动释放(用 delete) |
| 效率 | 高 | 低 |
| 适用场景 | 局部变量、数组 | 动态大小的数据 |
(2) 1.2 生活中的类比
| 生活场景 | 程序中的对应 |
|---|---|
| 快餐店(固定菜单,点完就端走) | 栈 |
| 自助餐(想吃多少拿多少) | 堆 |
2. new 和 delete
(1) 2.1 用 new 分配内存
语法:
▶ 示例 2:动态内存管理(难度⭐)
Type* pointerName = new Type;
输出:
(程序输出)
示例:
#include <iostream>
int main() {
// 在堆上分配一个 int
int* p = new int; // p 指向堆上的 int
*p = 5; // 给堆上的 int 赋值
std::cout << "*p = " << *p << std::endl; // 5
return 0;
}
💡 重点: new 返回的是指针——指向堆上分配的内存。
(2) 2.2 用 delete 释放内存
语法:
delete 指针名;
示例:
#include <iostream>
int main() {
int* p = new int;
*p = 5;
std::cout << "*p = " << *p << std::endl;
delete p; // ✅ 释放堆上的内存
p = nullptr; // ✅ 好习惯:释放后设为空指针
return 0;
}
💡 黄金法则: new 和 delete 必须成对出现——有 new 就有 delete。
(3) 2.3 忘记 delete 会内存泄漏
错误示例:
#include <iostream>
void foo() {
int* p = new int;
*p = 5;
// ❌ 忘记写 delete p;
} // 函数结束,p 被销毁,但堆上的 int 没被释放(内存泄漏)
int main() {
for (int i = 0; i < 1000000; i++) {
foo(); // 每次调用都泄漏 4 字节
}
// 程序占用内存会越来越大,最终可能崩溃
return 0;
}
💡 内存泄漏: 堆上分配的内存没有被释放,也没办法再访问(因为指向它的指针已经销毁了)。
3. 动态数组
(1) 3.1 用 new 分配数组
语法:
Type* pointerName = new Type[size];
示例:
#include <iostream>
int main() {
int n;
std::cout << "请输入数组大小:";
std::cin >> n;
// 在堆上分配一个能存 n 个 int 的数组
int* arr = new int[n];
// 使用数组
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// 输出数组
for (int i = 0; i < n; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// 释放数组(注意:用 delete,不是 delete)
delete arr;
return 0;
}
💡 重点: 释放数组要用 delete(带方括号),不是 delete!
(2) 3.2 new 和 delete 必须配对
| 分配 | 释放 |
|---|---|
new int |
delete p; |
new int[10] |
delete p; |
错误示例:
int* p = new int[10];
delete p; // ❌ 错误:应该用 delete p;
4. 悬空指针(Dangling Pointer)
(1) 4.1 什么是悬空指针?
悬空指针是指向已释放内存的指针。
#include <iostream>
int main() {
int* p = new int(5);
delete p; // 释放内存
// ❌ 危险:p 现在是悬空指针
std::cout << *p << std::endl; // 未定义行为!可能输出垃圾值,或程序崩溃
return 0;
}
💡 解决方法: 释放后立即设为 nullptr:
delete p;
p = nullptr; // ✅ 好习惯
5. 实战:动态数组排序
▶ 示例 1:用动态数组实现选择排序(难度⭐⭐)
#include <iostream>
// 选择排序
void selectionSort(int* arr, int n) {
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
// 交换
int temp = arr[i];
arr[i] = arr[minIdx];
arr[minIdx] = temp;
}
}
int main() {
int n;
std::cout << "请输入数组大小:";
std::cin >> n;
int* arr = new int[n]; // 动态分配数组
std::cout << "请输入 " << n << " 个整数:" << std::endl;
for (int i = 0; i < n; i++) {
std::cin >> arr[i];
}
selectionSort(arr, n); // 排序
std::cout << "排序后:" << std::endl;
for (int i = 0; i < n; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete arr; // 释放内存
arr = nullptr;
return 0;
}
输出:
请输入数组大小:
请输入 个整数:
排序后:
运行效果:
请输入数组大小:5
请输入 5 个整数:
5 3 1 4 2
排序后:
1 2 3 4 5
6. 常见错误
(1) 6.1 重复 delete
错误示例:
int* p = new int(5);
delete p;
delete p; // ❌ 错误:重复释放同一块内存(未定义行为)
修复: 释放后立即设为 nullptr(delete nullptr 是安全的)。
delete p;
p = nullptr;
(2) 6.2 忘记 delete
错误示例:
int* arr = new int[10];
// ❌ 忘记写 delete arr;
后果: 内存泄漏。
❓ 常见问题
▶ 示例 3:动态数组(难度⭐)
#include <iostream>
int main() {
int size = 5;
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete[] arr;
return 0;
}
输出:
0 10 20 30 40
new int[n] 分配数组,delete[] 释放数组。注意 [] 不能漏!
- 栈:编译时分配,自动释放,大小固定
- 堆:运行时分配,手动释放(
delete),大小不固定 new分配内存,delete释放内存new分配数组,delete释放数组- 内存泄漏:忘记
delete,后果严重 - 悬空指针:指向已释放的内存,危险
📖 小节
- new/delete:手动分配/释放内存
- new[]/delete[]:数组内存分配/释放
- 内存泄漏:忘记释放导致资源浪费
- 智能指针:推荐用 unique_ptr/shared_ptr 替代
📝 作业
-
基础题 (Difficulty ⭐): 用
new分配一个double,赋值3.14,输出后释放。 -
进阶题 (Difficulty ⭐⭐): 让用户输入
n,用new动态分配一个大小为n的int数组,输入n个整数,找出最大值后释放。 -
挑战题 (Difficulty ⭐⭐⭐): 用动态内存实现一个"动态数组"类(后面会学类,这里先用结构体):
-
包含:
int* data(指向堆上的数组)、int size(当前大小)、int capacity(容量) -
实现
push_back函数:如果size == capacity,就重新分配一块更大的内存(new更大的数组,复制过去,释放旧的) -
测试:往里添加 100 个元素
- new 分配堆内存,返回指针
- delete 释放 new 分配的内存
- new 分配数组,delete 释放数组
- 内存泄漏:new 了忘记 delete
- 分配失败:new 抛出 std::bad_alloc 异常
7. 🚀 下一步
学会了动态内存分配,接下来我们学习 指针和引用的实战综合(第27课)—— 通过综合实例,巩固前面学的所有知识!