【问题标题】:2-D array in single malloc call单个 malloc 调用中的二维数组
【发布时间】:2012-06-08 17:59:54
【问题描述】:
int **arrayPtr; 
arrayPtr = malloc(sizeof(int) * rows *cols + sizeof(int *) * rows);

在上面的代码中,我们试图在单个malloc 调用中分配一个二维数组。 malloc 占用多个字节并为这些字节分配内存, 但是在上面的例子中,malloc怎么知道首先要分配一个指针数组,每个指针都指向一个一维数组?

malloc 在这种特殊情况下如何在内部工作?

【问题讨论】:

  • 请定义您要对代码执行的操作。我的意思是为什么是双指针?而 malloc 是一个非常愚蠢的函数。它只是返回你请求的内存量(除非它失败)

标签: c multidimensional-array malloc


【解决方案1】:

二维数组与指向数组的指针数组不同。

int **arrayPtr 没有定义二维数组。二维数组如下所示:

int array[2][3]

指向该数组第一个元素的指针如下所示:

int (*array)[3]

你可以指向一块内存:

int (*array)[3] = malloc(sizeof(int)*5*3);


注意它是如何编入索引的:

  • array[x] 将扩展为 *(array+x),因此“向前 3 个整数的 x 个数组”。
  • array[x][y] 将扩展为 *( *(array+x) + y),所以“然后 y 向前”。

这里没有直接的指针数组,只有一个连续的内存块。

如果您有一个数组数组(与二维数组不同,通常使用 int** ptr 和一系列每行 malloc 完成),它会像:

  • ptr[x] 将扩展为 *(array+x),因此“x 指针向前”
  • ptr[x][y] 将扩展为 *( *(array+x) + y) = "y ints forward"。

注意区别。两者都使用 [x][y] 进行索引,但它们在内存中以不同的方式表示,并且索引以不同的方式发生。

【讨论】:

    【解决方案2】:

    malloc 怎么知道它首先要分配一个指针数组,每个指针都指向一个一维数组?

    它没有; malloc 只是分配您指定的字节数,它不知道如何将这些字节构造成聚合数据类型。

    如果您尝试动态分配多维数组,您有多种选择。

    如果您使用支持可变长度数组的 C99 或 C2011 编译器,您可以简单地将数组声明为

    int rows;
    int cols;
    ...
    rows = ...;
    cols = ...;
    ...
    int array[rows][cols];
    

    不过,VLA 存在许多问题;它们不适用于非常大的数组,它们不能在文件范围内声明,等等。

    第二种方法是执行以下操作:

    int rows;
    int cols;
    ...
    rows = ...;
    cols = ...;
    ...
    int (*arrayPtr)[cols] = malloc(sizeof *arrayPtr * rows);
    

    在这种情况下,arrayPtr 被声明为指向带有 cols 元素的 int 数组的指针,因此我们将分配 rows 数组中的每个 cols 元素。请注意,您只需编写 arrayPtr[i][j] 即可访问每个元素;指针算术规则的工作方式与常规二维数组相同。

    如果您使用的不是支持 VLA 的 C 编译器,则必须采用不同的方法。

    您可以将所有内容分配为单个块,但您必须将其作为一维数组访问,计算偏移量如下:

    int *arrayPtr = malloc(sizeof *arrayPtr * rows * cols);
    ...
    arrayPtr[i * rows + j] = ...;
    

    或者你可以分两步分配:

    int **arrayPtr = malloc(sizeof *arrayPtr * rows);
    if (arrayPtr)
    {
      int i;
      for (i = 0; i < rows; i++)
      {
        arrayPtr[i] = malloc(sizeof *arrayPtr[i] * cols);
        if (arrayPtr[i])
        {
          int j;
          for (j = 0; j < cols; j++)
          {
            arrayPtr[i][j] = some_initial_value();
          }
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      malloc() 不知道它需要分配一个指向数组的指针数组。它只是返回一块请求大小的内存。您当然可以通过这种方式进行分配,但是您需要初始化将用作指针的第一个“行”(或最后一个,甚至是列而不是行 - 但是您想这样做),以便它们指向该块中的适当区域。

      这样做会更好,更有效率:

      int *arrayPtr = malloc(sizeof(int)*rows*cols);
      

      这样做的缺点是您必须在每次使用时计算正确的索引,但您可以编写一个简单的辅助函数来做到这一点。你不会有使用[] 引用元素的“便利”,但你可以拥有例如element(arrayPtr, x, y).

      【讨论】:

        【解决方案4】:

        我会将您的注意力转移到“[] 运算符是做什么的?”的问题上。

        如果您打算通过 [] 运算符访问数组中的元素,那么您需要意识到它只能根据元素的大小进行偏移设置,除非提供了一些数组几何信息。

        malloc 没有维度信息的规定,calloc - 明确的一维。 另一方面,声明的数组 (arr[3][4]) 显式地为编译器指定维度。

        因此,要以 arr[i][j] 的方式访问动态分配的多维数组,实际上是分配了目标维度大小的一维数组系列。您将需要循环来执行此操作。

        malloc 返回指向堆内存的普通指针,没有关于几何或数据类型的信息。因此 [][] 不起作用,您需要手动进行偏移。

        因此,无论 []-indexing 是您的优先级还是批量分配,都由您决定。

        【讨论】:

          【解决方案5】:

          int **arrayPtr; 不指向二维数组。它指向一个指向int 的指针数组。如果要创建二维数组,请使用:

          int (*arrayPtr)[cols] = calloc(rows, sizeof *arrayPtr);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-30
            • 1970-01-01
            • 2019-01-26
            • 2013-11-30
            • 1970-01-01
            • 1970-01-01
            • 2016-08-21
            • 1970-01-01
            相关资源
            最近更新 更多