在堆区申请二维数组的方法

问题引入

  在做题的时候需要在堆区申请一个二维数组。所以当时很自然用这种方式来申请: int *a = new int[row][col]; ,编译器会报错。

  首先,有个错误是我把二维数组名理解成一个一级指针。这是因为之前打印输出二维数组名的地址时,二维数组名就是一个指向二维数组第一个元素地址的指针,所以错误地把二维数组名理解成是一个一级指针。

  然后,有人会说,用二级指针啊: int **a = new int[row][col]; ,编译器同样会报错。不可以把二维数组名理解为一个二级指针。

 

二维数组名究竟是什么

  先说结论,如果有二维数组int a[row][col]二维数组名a其实是一个指向int[col]这一数据类型的指针而a这个变量的类型是int(*)[col]。这里的int[col],是指数据类型为int[col]的一维数组,换句话说,这个一维数组的每一个元素都是int[col]型

  所以,我们可以尝试用这种方式来申请一个二维数组: int row = 10, col = 10; int (*a)[col] = new int[row][col]; ,这看上去没有什么问题,正如我们上面所说的,如果用new来申请一个二维数组,那么会返回一个int(*)[col]类型的指针,同时我们用int (*a)[col]来接收返回的地址(这也意味着a是一个指针,指向int[col]这种数据类型),可是编译器会有如下报错:

[Error] array size in new-expression must be constant
[Error] the value of 'col' is not usable in a constant expression
[Note] 'int col' is not const

  编译器说,col没有用const修饰,所以无法确定数组大小,从而无法申请这个二维数组。也就是说,要在堆区创建一个二维数组二维数组的第一维必须是一个常量

  这是因为,在C++中并不存在所谓的“二维数组”,所谓的二维数组,或者是多维数组实际上都是一维数组组成的。对于二维数组a[row][col]来说它的每一个元素的数据类型实际上是一种一维数组也就是int[col]。而col的值不同,与之对应的int[col]这种类型也会不同,编译器要确定的col值才可以确认int[col]到底是什么类型。比如int[3][4],和int[3][5],前者的每一个元素的数据类型是int[4],后者的每一个元素的数据类型是int[5],而且int[4]int[5]是两种不同的数据类型

  所以,对于col这个普通变量, int (*a)[col] = new int[row][col]; 这种方式并不可以申请一个二维数组,因为编译器无法确定int[col]是什么类型的。

  所以要用这种方式来申请二维数组,必须要让编译器知道int[col]是什么类型的,也就是说col必须是一个常量。这两种方法都是对的: int row = 10; const int col = 10; int (*a)[col] = new int[row][col]; , int row = 10; int (*a)[10] = new int[row][10]; ,编译器可以确认这个二维数组每一个元素的数据类型都是int[10]。

  至于为什么在栈区int a[row][col]; 可以申请二维数组,而在堆区不可以用这种方式来申请二维数组,我还不知道,如果你知道欢迎在评论区留言。

 

在堆区申请二维数组的方法

  其实上面已经知道了一种方法,那就是确认col的大小来申请二维数组,但这种方法的局限性很大。

  还可以通过二级指针来申请二维数组。

 1 #include <cstdio>
 2 
 3 int main() {
 4     int row = 5, col = 5;
 5     
 6     int **a = new int*[row];        // 先申请一个指针数组 
 7     for (int i = 0; i < row; i++) {
 8         a[i] = new int[col];        // 再申请row个大小为col的一维数组 
 9     }
10     
11     for (int i = 0; i < row; i++) {
12         for (int j = 0; j < col; j++) {
13             a[i][j] = 1;
14             printf("%d  ", a[i][j]);
15         }
16         putchar('\n');
17     }
18     
19     for (int i = 0; i < row; i++) {
20         delete[] a[i];              // 先释放掉一维数组 
21     }
22     delete[] a;                     // 再释放掉指针数组 
23     
24     return 0;
25 }

  运行结果如下:

   a[i][j]这种表述看上去是不是很像一个二维数组?其实它是有迷惑性的。按照上面申请内存的过程,实际上我们申请的这个“二维数组”的内存应该是这样子的:

   只不过我们取元素所用的方法与二维数组的很像,但实际上这个二级指针所表示的二维数组的内存分配与在栈区创建的二维数组的内存分配(一块连续的内存)是完全不同的。

  还有一种方法,是通过malloc函数来申请的。

 1 #include <cstdio>
 2 #include <cstdlib> 
 3 
 4 int main() {
 5     int row = 5, col = 5;
 6     
 7     int (*a)[col] = (int(*)[col])malloc(sizeof(int) * row * col);
 8     
 9     for (int i = 0; i < row; i++) {
10         for (int j = 0; j < col; j++) {
11             a[i][j] = 10;
12             printf("%d  ", a[i][j]);
13         }
14         putchar('\n');
15     }
16     
17     return 0;
18 }

  这里molloc函数返回的是int*型,我们需要把它强制类型转换为int(*)[col]型,并且让指向int[col]型的指针来接收,这种方式申请的二维数组的内存分配和在栈区创建的相同,都是一块连续的内存。

 

参考资料

  C++用new创建二维数组的方法:https://blog.csdn.net/samuelcoulee/article/details/8674388

  用malloc动态申请一个二维数组的三种方法:https://blog.csdn.net/fengxinlinux/article/details/51541003

posted @ 2021-04-01 22:20  onlyblues  阅读(1952)  评论(1编辑  收藏  举报
Web Analytics