首页 > 解决方案 > C、多维数组:元素为一维数组的数组?

问题描述

这句话是否有意义,来自《C Programming: A Modern Approach, 2nd Edition 》一书,第 269 页

就像一维数组的名称可以用作指针一样,任何数组的名称也可以用作指针,无论它有多少维。不过,需要一些小心。考虑以下数组:

int a[NUM_ROWS][NUM_COLS];

a相反,它不是指向指针,而是指向. 如果我们从 C 的角度来看,这更有意义,C不是二维数组,而是元素为一维数组的一维数组。当用作指针时,具有类型(指向长度为整数数组的指针)。a[0][0];a[0]aaint (*) [NUM_COLS]NUM_COLS

我很困惑,因为当我想到“其元素是一维数组的数组”时,我认为是一个jagged-array,但这不是这里发生的事情。这更像是一个带有指针算术的宏?

这是关于类型系统以及它如何处理多维数组的参考吗?有人能解释一下吗?

标签: carrayspointersmultidimensional-array

解决方案


是的,这是有道理的,不,它甚至没有谈论“参差不齐”或“参差不齐”的数组。只是当我们说

int a[NUM_ROWS][NUM_COLS];

我们正在创建的是一个数组a,而它的数组是......其他数组。你可以这样想:

        +---------------------------------------+
        | +--------+--------+--------+--------+ |
a: [0]: | |        |        |        |        | |
        | +--------+--------+--------+--------+ |
        +                                       +
        | +--------+--------+--------+--------+ |
   [1]: | |        |        |        |        | |
        | +--------+--------+--------+--------+ |
        +                                       +
        | +--------+--------+--------+--------+ |
   [2]: | |        |        |        |        | |
        | +--------+--------+--------+--------+ |
        +---------------------------------------+

(这里NUM_COLS显然是 4,而且NUM_ROWS是 3。)

二维(或更多)维数组 100% 类似于简单的一维数组——您只需要仔细考虑类比即可。如果a是一个数组,那么a在需要其值的表达式中的任何提及都会导致指向数组第一个元素的指针&a[0]a所以给定我们正在讨论的二维数组, a' 的值是&a[0]并且是指向整数数组的指针NUM_COLS

如果多维数组下标要正常工作,它必须以这种方式工作。如果我们写a[i][j],那将被解释为(a[i])[j]a像往常一样变成指向数组第一个元素的指针,但a[i]等效于*(a + i),其中指针算术最终被指向元素的大小缩放 - 也就是说,在引擎盖下,它更像*(a+ i * sizeof(*a)). 所以sizeof(*a)必须是sizeof(int [NUM_COLS]),或者NUM_COLS * sizeof(int)。这样a[i]你就可以得到第i'th 个子数组,然后j可以选择子数组的一个单元格 -大小的int单元格。

最后一点:我已经通俗地谈论过“多维数组”,但严格来说,正如这里的许多常客喜欢指出的那样,C 没有多维数组。它只有一维数组,而我们认为的二维数组实际上是我们在这里看到的一维数组,其元素恰好是其他一维数组。(如果 C 有真正的多维数组,则下标可能看起来像a[i,j]而不是a[i][j].)


附录:尽管您提到了指针算术,而我也提到了指针算术,但重要的是要意识到' 定义中不涉及指针a。只有当我们尝试“取值”a或解释如何a[i]等价于时,才会出现指针*(a + i)

对于确实涉及指针的数据结构,我们可以对比代码描述的情况

int *a2[NUM_ROWS];
for(i = 0; i < NUM_ROWS; i++)
    a2[i] = malloc(NUM_COLS * sizeof(int));

这给了我们一个非常不同的内存布局:

    +-----+
a2: |     |     +--------+--------+--------+--------+
    |  *------->|        |        |        |        |
    |     |     +--------+--------+--------+--------+
    +-----+
    |     |     +--------+--------+--------+--------+
    |  *------->|        |        |        |        |
    |     |     +--------+--------+--------+--------+
    +-----+
    |     |     +--------+--------+--------+--------+
    |  *------->|        |        |        |        |
    |     |     +--------+--------+--------+--------+
    +-----+

这就是通常所说的“参差不齐”或“参差不齐”的数组,因为在这种情况下,显然不需要所有行都具有相同的长度。然而,几乎神奇的是,“参差不齐的”数组中的单元格也可以使用a2[i][j]符号来访问。为了获得充分的活力,我们可以使用

int **a3 = malloc(NUM_ROWS * sizeof(int *));
for(i = 0; i < NUM_ROWS; i++)
    a3[i] = malloc(NUM_COLS * sizeof(int));

导致这种内存布局:

    +-----+
a3: |     |
    |  *  |
    |  |  |
    +--|--+
       |
       |
       V
    +-----+
    |     |     +--------+--------+--------+--------+
    |  *------->|        |        |        |        |
    |     |     +--------+--------+--------+--------+
    +-----+
    |     |     +--------+--------+--------+--------+
    |  *------->|        |        |        |        |
    |     |     +--------+--------+--------+--------+
    +-----+
    |     |     +--------+--------+--------+--------+
    |  *------->|        |        |        |        |
    |     |     +--------+--------+--------+--------+
    +-----+

也在这里a3[i][j]工作。

(当然,在构建像a2and这样的“动态数组”的实际代码中a3,我们必须检查以确保它malloc没有返回NULL。)


推荐阅读