c语言定义一个二维数组,并给二维数组赋值(C++ 使用 new 创建二维数组)
1. 直接创建
C++ 使用 new 创建二维数组最直接的方法就是 new T[M][N] 。返回的指针类型是 T (*)[N] ,它是指向数组的指针 ,可以直接使用数组下标形式访问元素 。释放内存直接使用delete[] 。示例代码:
#include <iostream> class A { public: A() { std::cout << "A::A" << std::endl; } ~A() { std::cout << "A::~A" << std::endl; } int x; }; int main() { A (*p)[3] = new A[2][3]; delete[] p; }执行结果:
A::A A::A A::A A::A A::A A::A A::~A A::~A A::~A A::~A A::~A A::~A可以看到 A 的构造函数和析构函数正常执行 。如果觉得 T (*)[N] 繁琐 ,可以直接使用 auto p = new T[M][N] 。三维数组甚至更高维数组都可以使用这种方法 。例如 ,三维数组使用 new T[M][N][O] 进行创建 ,依旧使用 delete[] p 进行释放 。
为什么可以这样写?因为这种多维数组和普通的多维数组都是通过一维数组实现的 。例如 ,int a[6][8] ,实际上编译器会转化为 int b[6 * 8] 一维数组 。然后每次访问二维数组 a[i][j] 相当于访问 b[i * 8 + j] 。从二维 、三维数组的转化过程中可以发现一些规律 。
T a[M][N] --> T b[M * N], a[i][j] --> b[i * N + j] T a[M][N][O] --> T b[M * N * O], b[i][j][k] --> b[i * N * O + j * O + k]编译器进行下标转换时 ,并没有用到第 0 维的大小 ,而其它维的大小都是必须的 。这也就是为什么下面代码能正确执行。
int a[2][3]; int (*p)[3] = a;由于多维数组本质上是一维数组 ,所以释放内存都是 delete[] p ,而没有奇怪的 delete[][] 语法 。
2. 借助指针数组
还有一种方法就是先 new T*[M] 创建一个指针数组 ,其每个元素保存每一行的首个元素的地址,再使用 new T[N] 创建每一行 。示例代码如下:
A** p = new A*[2]; for (int i = 0; i < 2; ++i) { p[i] = new A[3]; } for (int i = 0; i < 2; ++i) { delete[] p[i]; } delete[] p;这种方法非常繁琐 ,首先 new T*[M] 不能写成 new (T(*)[M]) ,因为它是指针数组而不是数组指针。其次,需要对每一行调用 new T[N] 。释放内存时 ,要先使用 delete[] 释放每一行 ,再调用 delete[] 释放数组指针 。这几个步骤一步都不能错 ,不然就出现野指针或者内存泄漏 。这段代码我也是用 Address Sanitizer 和 Leak Sanitizer 检查一遍才写对 。
这种方法唯一的好处就是可以创建交错数组(Jagged Array) ,也就是每一行的大小不一样 。例如:
A **p = new A *[2]; p[0] = new A[3]; p[1] = new A[4]; for (int i = 0; i < 2; ++i) { delete[] p[i]; } delete[] p;3. 借助 std::vector
可以用 std::vector 对上面这种方法进行包装 ,使其更加易用 。示例代码如下:
std::vector<std::vector<int>> v{ std::vector<int>(3), std::vector<int>(4) }; std::cout << v[0].size() << " " << v[1].size() << std::endl;这段代码创建了一个二维数组 ,第 0 行有 3 个元素 ,第 1 行有 4 个元素 。这种方法既能创建交错数组 ,也不需要手动释放内存 。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!