对所有行的一个 malloc 调用如何为二维数组工作?

How one malloc call for all rows work for 2D array?

我知道我们可以用指针来实现动态多维数组,而且有很多种方法,有单指针也有双指针。但是在探索这个主题时,遇到了这段我无法理解头部和尾部的代码。谁能解释一下下面这段代码是如何工作的?

还请说明,

1) 为什么有必要将 r*sizeof(int*) 分配给 arr 而我们无论如何都要将内存分配给 arr[i] 作为 r*c*sizeof(int)。 2) 为什么需要arr[i] = *arr+c*i.

由于我对这种动态内存分配非常陌生,并且非常渴望深入挖掘,因此出现了这些问题。对不起,如果它是基本的,但我仍然不知道。 谢谢,

#include<stdio.h>
#include<stdlib.h>

int main()
{
    int r=3, c=4;
    int **arr;
    int count = 0,i,j;

    arr  = (int **)malloc(sizeof(int *) * r);
    arr[0] = (int *)malloc(sizeof(int) * c * r);

    for(i = 0; i < r; i++)
        arr[i] = (*arr + c * i);

    for (i = 0; i < r; i++)
        for (j = 0; j < c; j++)
            arr[i][j] = ++count;  // OR *(*(arr+i)+j) = ++count

    for (i = 0; i <  r; i++)
        for (j = 0; j < c; j++)
        {
            printf("%d, %p, %p\n", arr[i][j], &arr[i][j], arr[i]);
        }

    return 0;
}

输出:

1, 21100, 21100
2, 21104, 21100
3, 21108, 21100
4, 2110c, 21100
5, 21110, 21110
6, 21114, 21110
7, 21118, 21110
8, 2111c, 21110
9, 21120, 21120
10, 21124, 21120
11, 21128, 21120
12, 2112c, 21120

不是为 arr 中分配的每个 r 指针分配内存,而是仅第一个用于为 rxc 数组分配内存。其余指针指向该内存块。

好处是可以使用单个 memset 来初始化数组。释放要容易得多(只需释放第一个指针分配的内存)。

arr[i] = (*arr + c * i); 这基本上是用它应该指向的相关部分初始化指针 arr[i]

从分配的内存开始,它会在哪里? 有 arr[0],arr[1]..arr[i-1] 指针指向它们的行,每个行包含 c 个元素。因此 c 个元素每个 i 指针 - i*c 个元素一起已经被处理。所以下一个被arr[i]指向的会是(*arr+c*i).

OP 编辑​​问题后:

OP 问为什么我们需要做 arr = (int **)malloc(sizeof(int *) * r) ?

我想这张图片比文字更能说明问题。

arr --> [0]  [1]  [2]   [3]  .....[r-2] [r-1]
         |    |    |     |           |    |
         V    |    |     |           |    |
        [0] <-+    |     |           |    |
        [1]        |     |           |    |
        [2]        |     |           |    |
        [3]        |     |           |    |
        [4]        |     |           |    |
         .         |     |           |    |
                   |     |           |    |
        [c-1]      |     |           |    |
        [c]   <----+     |           |    |
        [c+1]            |           |    |
        [c+2]            |           |    |
         .               |           |    |
         .               |           |    |
         .               |           |    |
        [2c]  <----------+           |    |
        [2c+1]                       |    |
        [2c+2]                       |    |
         .                           |    |
         .                           |    |
         .                           |    |
        [(r-2)*c] <------------------+    |
        [(r-2)*c+1]                       |
         .                                |
         .                                |
        [(r-2)*c+(c-1)]                   |
        [(r-1)*c]  <----------------------+
        [(r-1)*c+1]
        [(r-1)*c+2]
        [(r-1)*c+3]


        [(r-1)*c+(c-1)]~[rc-1] 

第一行解释 arr = malloc(sizeof(int *) * r);

您可以在单列中看到所有分配的内存。因为那是你所做的 arr[0] = (int *)malloc(sizeof(int) * c * r);

然后链接说明arr[i] = (*arr + c * i);

检查一下,链接指向 (*arr) 也在图片 [0]

and (*arr+c) in pic [c] and (*arr+2c) in pic [2c].

我们需要它们,因为它基本上让我们到达每个 r 行开始的正确地址的开始。

地址计算为从 *arr 开始的偏移量。

如果你没有将地址分配给 arr[i] 那么你就不能像这样访问数组 arr[row][col] 那么你必须做 arr[0][row*c+col] (你可以看到那个图像也说了那件事。

在 C 中,数组在内存中按 "row major" 顺序存储。这意味着在二维数组中,同一行中的元素与内存中的另一个元素相邻,因此同一列中的元素按列数(即行宽)隔开。

*arr 返回一个指向 int 的指针,因为它解引用了一个指向 int 指针的指针。具体来说就是名字arr指向的指针。那就是数组的基地址。

arr[i] 也是一个指向 int 的指针。现在请记住我所说的行专业。第 i 行指针应该指向哪里?答案是,在数组的底部 (*arr) 加上 i 乘以列数 (c)。使您感到困惑的循环只是让其他循环指向正确的位置,以便在使用 arr[r][c] 表示法时隐含的指针算法起作用。

瞧。

这段代码分配了两个内存区域

  • arr 能够包含 r 指向 int
  • 的指针
  • arr[0]面积,为r * c int
  • (*arr + c * i) 等同于 &arr[0][c*i]

然后每个 r(行)被分配一个指向 arr[0] 中由 c(列)int 间隔的位置的指针(用于存储 c int)

for(i = 0; i < r; i++)
    arr[i] = (*arr + c * i); 

图形化(r == 3,c == 4),P 指针,I 整数

   arr: 0:P 1:P 2:P
        |   |   |
        v   v   v
arr[0]: IIIIIIIIIIII

然后它将数组视为 arr[row][col]

for (i = 0; i < r; i++)
        for (j = 0; j < c; j++)
            arr[i][j] = ++count;