【问题标题】:C, Multidimensional arrays: array whose elements are one-dimensional arrays?C、多维数组:元素为一维数组的数组?
【发布时间】:2019-01-15 04:47:47
【问题描述】:

这句话有意义吗,来自书 C Programming: A Modern Approach, 2nd Edition 的第 269 页

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

int a[NUM_ROWS][NUM_COLS];

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

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

这是否涉及类型系统以及它如何处理多维数组?谁能解释一下?

【问题讨论】:

  • 要理解这句话,您需要彻底理解声明 int (*ptr)[7];,它创建了一个指向 7 整数数组的指针。不要与 int *ptr[7]; 混淆,后者创建了一个包含 7 个指向 int 的指针的数组。本书前面应该详细讨论过这种区别。

标签: c arrays pointers multidimensional-array


【解决方案1】:

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

int a[NUM_ROWS][NUM_COLS];

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

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

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

二维(或更多)维数组 100% 类似于简单的一维数组——您只需要仔细考虑类比即可。如果a 是一个数组,那么在需要其值的表达式中对a 的任何提及都会导致指向数组第一个元素&a[0] 的指针。因此,鉴于我们正在讨论的二维数组aa 的值是&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] 也在这里工作。

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

【讨论】:

    【解决方案2】:

    另一种看待它的方式......

    对于任何类型T,我们创建一个数组为

    T arr[N];
    

    其中T 可以是intchardoublestruct foo 等等,读作“T 的 N 元素数组”。它也可以是另一个数组类型。因此,假设Tint 的一个M 元素数组,而不只是int,我们将其写为

    int arr[N][M];
    

    这读作“arrint 的 M 元素数组的 N 元素数组”。这不是一个锯齿状数组——所有“行”的大小都相同。但它也不完全是一个二维数组——它是一个数组数组。表达式 arr[i] 具有数组类型 (int [M])。

    这个视图也帮助我们找出指向数组类型的指针。除非它是 sizeof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化字符数组的字符串字面量,否则 表达式 类型为“N 元素数组T” (T [N]) 将被转换 (“decay”) 为类型为“pointer to T” (T *) 的表达式。同样,如果您将 T 替换为数组类型 int [M],那么您将得到一个类型为“int 的 M 元素数组的 N 元素数组”(int [N][M]) 的表达式,该类型“衰减” “指向 int 的 M 元素数组的指针”(int (*)[M])。

    【讨论】:

    • 我们可以全力以赴,将指向 arr 的指针作为 (int (**)[M]) 传递。很好的解释。
    猜你喜欢
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    • 1970-01-01
    相关资源
    最近更新 更多