C++ 动态分配数组指南

除动态分配单个变量外,我们亦可动态分配变量数组。与固定数组须在编译期确定长度不同,动态分配允许在运行期指定数组长度(即长度无需为 constexpr)。

作者注: 以下课程将使用动态分配的 C 风格数组,这是最常见的动态数组形式。
虽然可以动态分配 std::array,但通常更推荐使用非动态分配的 std::vector。

动态分配数组

要动态分配数组,应使用数组形式的 new 与 delete(常写作 new[] 与 delete[]):

#include <cstddef>
#include <iostream>

int main()
{
    std::cout << "请输入一个正整数: ";
    std::size_t length{};
    std::cin >> length;

    ```cpp
    int* array{ new int[length]{} }; // 使用数组 new。注意 length 无需为常量!
std::cout << "我已分配了一个长度为 " << length << " 的整数数组\n";

array[0] = 5; // 将第 0 个元素设为 5

delete[] array; // 使用数组 delete 释放数组

// 由于 array 立即离开作用域,无需再置为 nullptr

return 0;

}


由于我们分配的是数组,C++ 会调用数组版 new(即 new[]),尽管语法上 [] 并非紧挨 new 关键字。  
动态数组长度类型为 std::size_t。若使用非常量 int,需通过 static_cast 转换,否则编译器会报窄化转换警告。  

注意:动态数组内存来源不同于固定数组,故数组长度可以非常大。您可以尝试分配长度为 1 000 000(甚至 100 000 000)的数组而不会出错。因此,需要大量内存的 C++ 程序通常采用动态分配。  

## 动态释放数组  
释放动态数组时必须使用数组版 delete,即 delete[]。  
这告知 CPU 需清理多个变量而非单个变量。初学者最常犯的错误之一便是对动态数组使用 delete 而非 delete[]。对数组使用标量 delete 会导致未定义行为,如数据损坏、内存泄漏、崩溃等。  

常见问题:“数组 delete[] 如何知道要释放多少内存?”  
答:数组 new[] 会记录分配量,供数组 delete[] 正确释放。遗憾的是,该长度对程序员不可见。  

## 动态数组与固定数组几乎相同  
在 《C 风格数组退化》中,您已了解固定数组保存首元素地址,并可退化为指向首元素的指针。退化后,数组长度不可用(sizeof 亦无法求得大小),除此之外差异甚微。  
动态数组本质上是指向首元素的指针,因此同样不知长度,功能与退化后的固定数组一致,区别仅在于程序员需用 delete[] 手动释放。  

## 初始化动态分配数组  
若要将动态数组初始化为 0,语法如下:  

```cpp
int* array{ new int[length]{} };

C++11 之前,无法便捷地将动态数组初始化为非零值(初始化列表仅适用于固定数组),需手动循环赋值:

int* array = new int[5];
array[0] = 9;
array[1] = 7;
array[2] = 5;
array[3] = 3;
array[4] = 1;

自 C++11 起,可使用初始化列表:

int fixedArray[5] = { 9, 7, 5, 3, 1 };   // C++11 之前初始化固定数组
int* array{ new int[5]{ 9, 7, 5, 3, 1 } }; // C++11 起初始化动态数组
// 为避免写两次类型,可用 auto
auto* array{ new int[5]{ 9, 7, 5, 3, 1 } };

注意:语法中数组长度与初始化列表之间无 = 运算符。
为保持一致,固定数组亦可使用统一初始化:

int fixedArray[]{ 9, 7, 5, 3, 1 };
char fixedArray[]{ "Hello, world!" };

显式指定数组大小可选。

调整数组大小

动态分配允许在分配时设定长度,但 C++ 未提供内置机制以调整已分配数组的大小。若要扩容,只能重新申请新数组,复制元素后释放旧数组。然而,这一过程易出错,尤其当元素为类类型时(类对象的创建有特殊规则)。
因此,建议直接使用 std::vector 而非手动实现。

测验

问题 #1
编写程序,完成以下功能:

  1. 询问用户欲输入多少个姓名;
  2. 动态分配 std::string 数组;
  3. 提示用户逐条输入姓名;
  4. 调用 std::sort 排序姓名(参见 《使用选择排序对数组排序》与 《指针运算与下标》);
  5. 输出排序后的姓名列表。

std::string 已支持通过运算符 < 与 > 比较,无需手动实现。

输出示例:

How many names would you like to enter? 5
Enter name #1: Jason
Enter name #2: Mark
Enter name #3: Alex
Enter name #4: Chris
Enter name #5: John

Here is your sorted list:
Name #1: Alex
Name #2: Chris
Name #3: Jason
Name #4: John
Name #5: Mark

提示

  • 使用 std::getline() 读取含空格的姓名(参见 5.7《std::string 简介》)。
  • 对指针形式的数组调用 std::sort 时,需手动计算首尾迭代器:

std::sort(array, array + arrayLength);

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"