Q: 6.16 我如何动态分配一个多维数组?


A: 传统的方法是分配一个到指针的指针的数组,然后初始每个指针到一个动态分配的“行”。这里是一个两维数组的例子:

  #include 

  int **array1 = malloc(nrows * sizeof(int *));
  for(i = 0; i < nrows; i++)
    array1[i] = malloc(ncolumns * sizeof(int));

(在真实的代码中,当然要检查所有malloc的返回值。你也可以使用sizeof(*array1)和sizeof(**array1)代替sizeof(int *)和sizeof(int)。)

你可以让数组的内容保持连续,所付出的代价是以后再分配单独行更困难,使用一些显式指针算术:

  int **array2 = malloc(nrows * sizeof(int *));
  array2[0] = malloc(nrows * ncolumns * sizeof(int));
  for(i = 1; i < nrows; i++)
    array2[i] = array2[0] + i * ncolumns;

在每一种情况(例如对array1和array2)中,动态数组的元素可以按正常的数组下标形式来访问:arrayx[i][j](对0 <= i < nrows并且0 <= j < ncolumns)。这是一个array1和array2的布局示意图:

          +--+        +--+--+--+--+--+
  array1: |o-+--------+  |  |  |  |  |
          +--+        +--+--+--+--+--+
          |o-+-----+
          +--+     |  +--+--+--+--+--+
          |o-+--+  +--+  |  |  |  |  |
          +--+  |     +--+--+--+--+--+
                |
                |     +--+--+--+--+--+
                +-----+  |  |  |  |  |
                      +--+--+--+--+--+

          +--+        +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  array2: |o-+--------+  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
          +--+        +--+--+--+--+--++-+--+--+--+--++-+--+--+--+--+
          |o-+------------------------+              |
          +--+                                       |
          |o-+---------------------------------------+
          +--+

如果上述图解中隐含的二次解引用,因为某些原因不可接受的话,你可以用一个动态分配的单个一维数组,来模拟一个二维数组:

  int *array3 = malloc(nrows * ncolumns * sizeof(int));

然而,现在你必须手工进行下标计算,访问第i,jth元素用这个表达式:

  array3[i * ncolumns + j]

并且这个数组并非一定要传递给一个要求多维数组的函数。(一个这样的宏

  #define Arrayaccess(a, i, j) ((a)[(i) * ncolumns + (j)])

可以隐藏明显的计算,但是调用它可能需要圆括号和逗号,这看起来不很像C传统的多维数组语法,并且这个宏也需要访问这些维度中至少一个。参见问答6.19。)

另一个选择是使用到数组的指针:

  int (*array4)[NCOLUMNS] = malloc(nrows * sizeof(*array4));

或者,甚至

  int (*array5)[NROWS][NCOLUMNS] = malloc(sizeof(*array5));

但是语法开始变得可怕(对array5访问是(*array5)[i][j] ),并且在运行时最多只能指定一个维度。

使用全部这些技巧,你当然应该需要记得,在不在需要时释放这些数组;在array1和array2的例子中,这需要几个步骤(参见问答7.23):

  for(i = 0; i < nrows; i++)
    free((void *)array1[i]);
  free((void *)array1);

  free((void *)array2[0]);
  free((void *)array2);

你也不必要混合动态分配数组,和传统的静态数组(参见问答6.20,和6.18)。

最后,你能在C99中使用变长数组(variable-length array)。

所有这些技巧也能被扩展到三或更多维数组.这里是第一个技巧的一个三维版本(像这里展示的其它代码片断一样,在用于真正的程序之前,它也需要错误检查):

  int ***a3d = (int ***)malloc(xdim * sizeof(int **));
  for(i = 0; i < xdim; i++) {
    a3d[i] = (int **)malloc(ydim * sizeof(int *));
    for(j = 0; j < ydim; j++)
      a3d[i][j] = (int *)malloc(zdim * sizeof(int));
  }

同时参见问答20.2。

参考:C9X Sec. 6.5.5.2


(This Chinese translation isn't confirmed by the author, and it isn't for profits.)

Translator : jhlicc@gmai1.c0m
Origin : http://www.c-faq.com/aryptr/dynmuldimary.html