如何在C语言(C89标准)中动态创建二维数组

C99标准的C语言支持可变长度数组,可以用变量作为数组长度创建二维数组。那么,在C89标准的C语言中如何实现动态创建二维数组?

用一维数组代替二维数组

内存是线性(一维)的,不管数组是几维,最终在内存中都是线性存储的。二维数组的内存模型如图:

内存模型

C语言通过计算与数组首地址的偏移字节数来定位到数组元素。例如在数组int array[M][N]中,访问元素array[i][j]时,计算地址的方式为:

1
2
3
4
5
6
单位字节数 = sizeof (int)
第二维每个元素的单位数 = 1
第一维每个元素的单位数 = 第二维每个元素的单位数 × 第二维的长度N
偏移单位数 = 第一维每个元素的单位数 × i + 第二维每个元素的单位数 × j
偏移字节数 = 单位字节数 × 偏移单位数
元素的地址 = 数组首地址 + 偏移字节数

由此想到,我们分配一维数组,然后自己计算偏移量(以sizeof (int)为单位),将二维数组下标转换为一维数组下标,就能正确访问到数组元素了。

1
2
3
int rows = 3, cols = 4;
int *array = malloc(sizeof (int) * rows * cols);
array[cols * i + j]; // 访问元素

缺点:不能用array[i][j]的语法访问数组元素,可读性较差。

规则的动态二维数组

  1. 先动态分配一个指针数组(第一维数组)
  2. 再为指针数组的每个指针分配元素数组(第二维数组)
1
2
3
4
5
6
int rows = 3, cols = 4;
int **array = malloc(sizeof (int *) * rows);
for (i = 0; i < rows; i++) {
array[i] = malloc(sizeof (int) * cols);
}
array[i][j]; // 访问元素

内存模型如图:

内存模型

可通过array[i][j]的语法访问数组元素,其本质是两级指针解引用,即等同于*(*(array + i) + j)

gcc -std=c89编译通过,运行结果正确。

Java语言的多维数组就是这种原理,即多级一维数组。

不规则的动态二维数组

如果分配第二维数组时,长度不固定,那么二维数组就不是规则的矩形,而是每行长度不同的不规则的形状。

1
2
3
4
5
6
7
int rows = 3;
int cols[] = {4, 2, 3};
int **array = malloc(sizeof (int *) * rows);
for (i = 0; i < rows; i++) {
array[i] = malloc(sizeof (int) * cols[i]);
}
array[i][j]; // 访问元素

内存模型如图:

内存模型

同样可以通过array[i][j]访问数组元素,只不过对于不同的ij的合法范围不同。

这种模式可以用来存储三角矩阵。