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:动态内存管理(难度⭐)

CPP
Type* pointerName = new Type;
▶ 试一试

输出:

TEXT 📖 仅展示
(程序输出)

示例:

CPP
#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 释放内存

语法:

TEXT 📖 仅展示
delete 指针名;

示例:

CPP
#include <iostream>

int main() {
 int* p = new int;
 *p = 5;
 
 std::cout << "*p = " << *p << std::endl;
 
 delete p; // ✅ 释放堆上的内存
 p = nullptr; // ✅ 好习惯:释放后设为空指针
 
 return 0;
}

💡 黄金法则: newdelete 必须成对出现——有 new 就有 delete


(3) 2.3 忘记 delete 会内存泄漏

错误示例:

CPP
#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 分配数组

语法:

TEXT 📖 仅展示
Type* pointerName = new Type[size];

示例:

CPP
#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;

错误示例:

CPP
int* p = new int[10];
delete p; // ❌ 错误:应该用 delete p;


4. 悬空指针(Dangling Pointer)

(1) 4.1 什么是悬空指针?

悬空指针是指向已释放内存的指针。

CPP
#include <iostream>

int main() {
 int* p = new int(5);
 delete p; // 释放内存
 
 // ❌ 危险:p 现在是悬空指针
 std::cout << *p << std::endl; // 未定义行为!可能输出垃圾值,或程序崩溃
 
 return 0;
}

💡 解决方法: 释放后立即设为 nullptr

CPP
delete p;
p = nullptr; // ✅ 好习惯


5. 实战:动态数组排序

▶ 示例 1:用动态数组实现选择排序(难度⭐⭐)

CPP
#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;
}
▶ 试一试

输出:

TEXT 📖 仅展示
请输入数组大小:
请输入  个整数:
排序后:
 

运行效果:

TEXT 📖 仅展示
请输入数组大小:5
请输入 5 个整数:
5 3 1 4 2
排序后:
1 2 3 4 5


6. 常见错误

(1) 6.1 重复 delete

错误示例:

CPP
int* p = new int(5);
delete p;
delete p; // ❌ 错误:重复释放同一块内存(未定义行为)

修复: 释放后立即设为 nullptrdelete nullptr 是安全的)。

TEXT 📖 仅展示
delete p;
p = nullptr;

(2) 6.2 忘记 delete

错误示例:

CPP
int* arr = new int[10];
// ❌ 忘记写 delete arr;

后果: 内存泄漏。


❓ 常见问题

Q 为什么要用动态内存?直接用数组不行吗?
A 如果编译时知道大小,用数组就行。但如果: > - 大小由用户输入决定 > - 大小可能很大(超过栈的容量) > - 需要让数据在函数结束后还存在

▶ 示例 3:动态数组(难度⭐)

CPP
#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;
}
▶ 试一试

输出:

TEXT 📖 仅展示
0 10 20 30 40
💡 提示new int[n] 分配数组,delete[] 释放数组。注意 [] 不能漏!



📖 小节

📝 作业

  1. 基础题 (Difficulty ⭐):new 分配一个 double,赋值 3.14,输出后释放。

  2. 进阶题 (Difficulty ⭐⭐): 让用户输入 n,用 new 动态分配一个大小为 nint 数组,输入 n 个整数,找出最大值后释放。

  3. 挑战题 (Difficulty ⭐⭐⭐): 用动态内存实现一个"动态数组"类(后面会学类,这里先用结构体):

  4. 包含:int* data(指向堆上的数组)、int size(当前大小)、int capacity(容量)

  5. 实现 push_back 函数:如果 size == capacity,就重新分配一块更大的内存(new 更大的数组,复制过去,释放旧的)

  6. 测试:往里添加 100 个元素


7. 🚀 下一步

学会了动态内存分配,接下来我们学习 指针和引用的实战综合(第27课)—— 通过综合实例,巩固前面学的所有知识!

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏