当数组长度要等到运行时才知道
假设要写一个统计成绩的程序。写代码时,我们并不知道这次会录入几名学生:可能是 3 人,也可能是 30 人。只有程序开始运行、用户输入人数以后,才能确定需要多少个元素。
之前的 int numbers[4]; 会预先准备 4 个整数的位置。把这个数字改成一个很大的常量虽然能多留一些位置,却可能浪费空间,也仍然有容量上限。我们希望先得到实际人数,再准备相应大小的一段存储空间。
这就是动态申请内存:程序运行到需要数据的时候,才请求一块大小合适的内存;使用完毕后,再主动归还。“动态”指申请时机和大小可以在运行时决定,并不是说数组会自动变长。
一块数组空间,与一个保存地址的指针
可以把内存想成许多带地址的格子。申请数组时,程序请求一段连续的格子来存放元素。申请成功后,得到的是第一个格子的地址,而不是把整个数组装进一个指针变量。
numbers 保存这段空间的首地址,numbers[0]、numbers[1] 等表达式则沿着地址访问其中的元素。指针变量和它指向的数组,是两处不同的存储空间。这种由程序主动申请、释放的区域通常称为堆。
在 C++ 中,new (nothrow) int[length] 申请 length 个整数的位置。nothrow 来自 <new>,使申请失败时返回 nullptr;因此必须先检查结果,再使用数组。
刚申请到的这些整数还没有初始化,不能当作已经录入的成绩来读取。接下来必须先写入每个元素,再进行统计。
示例:输入人数,再录入成绩
下面的程序先读取人数,将本例允许的范围限制在 1~1000,再申请对应长度的数组。之后逐项录入成绩并计算平均分;正常结束和录入失败的路径都会释放已经申请的空间。
#include <iostream>
#include <new>
using namespace std;
int main(void) {
int length;
cout << "Number of students (1-1000): ";
if (!(cin >> length) || length < 1 || length > 1000) {
cerr << "Invalid number of students.\n";
return 1;
}
int *numbers = new (nothrow) int[length];
if (numbers == nullptr) {
cerr << "Not enough memory.\n";
return 1;
}
for (int i = 0; i < length; i++) {
cout << "Score " << i + 1 << ": ";
if (!(cin >> numbers[i]) || numbers[i] < 0 || numbers[i] > 100) {
cerr << "Invalid score.\n";
delete[] numbers;
numbers = nullptr;
return 1;
}
}
double total = 0;
for (int i = 0; i < length; i++) {
total += numbers[i];
}
cout << "Average: " << total / length << endl;
delete[] numbers;
numbers = nullptr;
return 0;
}知识测验
运行时创建一份成绩数组
解锁完整互动实验、进阶解析与精选参考答案。
知识测验
使用完毕:归还空间,再清空指针
delete[] numbers; 归还的是动态数组的存储空间,不是删除局部变量 numbers。释放后不能再读取或修改这些元素,也不能重复释放同一块空间。
释放操作不会替你把 numbers 设为空指针。紧接着执行 numbers = nullptr;,明确表示它不再指向可用的数组。若另有指针也指向这块空间,它同样不能再使用,不会因为这里清空了 numbers 就自动变安全。
数组在本次申请期间的长度保持不变。想为另一批人数准备数组,应在原数组用完并释放后,根据新的输入重新申请。Lab 中可以重新运行、选择不同人数,比较每次出现的格子数量。
知识测验
创建平方数组
实现 int *make_squares(int length),动态创建 length 个整数并存入 1² 到 length²;失败时返回 nullptr。main 用长度 5 输出数组,然后释放内存。
- 检查 n 是否为正数。
- 申请 n 个整数并检查申请结果。
- 填入平方数、输出数组,然后正确释放并清空指针。
复制整数数组
实现 int *copy_array(const int *source, int length),申请新数组并逐项复制。用 {3, 1, 4, 1} 测试,打印副本并释放。
- 根据元素个数申请目标数组。
- 用循环复制每个整数,而不是只复制指针。
- 修改副本的一个元素,分别输出两个数组后释放目标内存。