【问题标题】:Dynamically Allocating 2D Arrays in C在 C 中动态分配二维数组
【发布时间】:2013-11-14 13:27:19
【问题描述】:

首先,我不是 C 编程方面的专家,我现在正在阅读一些遗留的 C 代码。在那里,我找到了以下用于初始化 2D 矩阵的函数:

long int **computeDistanceMatrix(void){
    long int i;
    long int j;
    long int ** matrix;
    matrix = malloc(sizeof(long int) * numberOfCities * numberOfCities +
        sizeof(long int *) * numberOfCities);

    if(matrix == NULL){
        fprintf(stderr, "Out of memory, exit.");
        exit(1);
    }

    for (i = 0; i < numberOfCities; i++){
        matrix[i] = (long int*) (matrix + numberOfCities) + i * numberOfCities;
        for (j = 0; j < numberOfCities; j++){
            matrix[i][j] = distanceFunction(i, j);
        }
    }
    return matrix;
}

我发现代码有点奇怪,因为我期待more similar to this 对我来说似乎更清楚。无论如何,我想知道以下几点:

  • malloc(sizeof(long int) * numberOfCities * numberOfCities + sizeof(long int *) * numberOfCities) 行表示他们正在为数据项和指向每一行的指针分配内存?
  • matrix[i] = (long int*) (matrix + numberOfCities) + i * numberOfCities 到底是什么意思?我真的不明白他们想要做什么。
  • 有没有更直接的方法在 C 中为二维数组分配内存,或者这是正确的方法?

【问题讨论】:

    标签: c pointers memory-management multidimensional-array


    【解决方案1】:

    据我所知,到目前为止,人们只提出了 2D 矩阵的仿真。

    自 C99 以来,C 具有可变长度数组的概念,即 VLA,它允许分配具有动态边界的适当 2D 矩阵。由于通常这些对于堆栈来说太大了,您必须通过 malloc 分配它们,如下所示:

    double (*A)[n] = malloc(sizeof(double[n][n]));
    

    没有复杂的指针方案,必要的,只需对连续对象进行一次分配。然后,您可以像 A[i][j] 一样简单地访问该矩阵的元素,编译器会为您完成所有索引计算。

    因此,无论何时(您没有强制使用指向指针的旧代码,您有一个支持 C99 的编译器),您都应该使用它。它更简单、更不容易出错且更高效。

    【讨论】:

    • 上一句你说的more efficient,从计算上来说,这种形式是不是更高效?为什么?
    • @yeyo,多种原因:只有一次调用malloc(和free)而不是多次调用,这是一个没有漏洞的连续分配,访问元素是简单的算术,无需重定向并且可以完成直接由大多数现代硬件。 gustedt.wordpress.com/2014/09/08/dont-use-fake-matrices
    • 完全有道理。谢谢你,我真的很感激。你的博客;太棒了!
    • @Jens Gustedt 非常有趣!为了确保我理解,double A[2][3][4] 会被声明为double (*A)[3][4] = malloc(sizeof(double[2][3][4])); 吗?
    【解决方案2】:

    鉴于sizeof (long int) = l、n 城市和sizeof (long int *) = p,这段代码试图做与the article you link to in your question 几乎相同的事情。只是,它尝试通过一次调用 malloc 而不是多次来实现。

    分配的第一个np 字节用于保存n 指针。这些指针中的每一个都指向一大块nl 长整数。 long int 块当然都是相邻的,第一个块本身与 n 的块相邻,p 大小的指针,并从地址 matrix + np 开始。

    行: matrix[i] = (long int*) (matrix + numberOfCities) + i * numberOfCities

    .. 将每个n 指针设置为指向块中的正确位置。并且希望下面的 ASCII 艺术可以清楚地说明这一点;)

    (addr = matrix +)  0    p    2p .. np   np+nl np+2nl
    
    [matrix]-------->[ v    v    v  .. ^    ^     ^
                       |    |    |     |    |     |
                       |____|____|_____|    |     |
                            |____|__________|     |
                                 |________________|
    

    我不知道这种“优化”(以降低可读性为代价)是否会带来优势。有些可能是:

    1. 它可能会减少 malloc 实现所需的簿记量。但也有额外的成本,因为我们现在自己计算指针值。

    2. 一个free调用就可以摆脱整个二维数组(当然,释放代码的程序员应该知道他/她必须调用一个free )。

    3. 可能有优势 w.r.t.缓存。阵列在一个块中,空间局部性发挥作用。另一方面,多个malloc 调用可能会返回彼此相距很远的块。

    【讨论】:

    【解决方案3】:

    创建二维数组的简单方法是:

    long** create2DArray(int rows, int cols)
    {
        // create an array of long* pointers
        long** ptr = (long**) malloc(sizeof(long*) * rows);
    
        for(int i = 0; i < rows; i++)
        {
            // allocate an array for each row
            ptr[i] = (long*)malloc(sizeof(long) * cols));
        }
        return ptr;
    }
    
    int main()
    {
        long int** ptr = create2DArray();
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      这是动态分配二维数组的另一种方法

      #include <stdlib.h>
      
      int **array;
      array = malloc(nrows * sizeof(int *));
      if(array == NULL)
          {
          fprintf(stderr, "out of memory\n");
          exit(EXIT_FAILURE); //or return here
          }
      for(i = 0; i < nrows; i++)
          {
          array[i] = malloc(ncolumns * sizeof(int));
          if(array[i] == NULL)
              {
              fprintf(stderr, "out of memory\n");
              exit(EXIT_FAILURE); //or return here
              }
          }
      

      主要思想是声明一个指向 int 指针的指针。这是您的矩阵(数组)。首先,您动态分配大小等于您想要的第一个维度。然后您动态分配另一个整数指针数组。

      您真正需要了解的是,当您在内存中动态分配空间时,没有“维度”。它们只是一些随机的地方。考虑到这一点,您可以分配一个 ND 维度数组并超越二维限制。

      【讨论】:

      • 这并不比 OP 更“正确”,它可能更容易阅读,但与原始版本相比它也有一些缺点(数据在内存中的布局与 OP 中的不同)。
      • 不过比较容易理解
      • Torner 你是对的。我很难理解发布的代码。现在对我来说似乎很好。没关系,我认为这里不需要使用 OP 。你真的不需要获得足够的这些。最后,您必须考虑什么对您作为程序员更有利(节省时间或尝试“放置”更好的内存?)。我认为这实际上取决于开发人员的选择。
      【解决方案5】:

      matrix 的第一个 numberOfCities 项是 long int*,它们指向允许写入 matrix[i][j] 以获取元素 (i,j) 的相应行。以下字节被视为long int。

      答案:

      1) 是的。

      2) matrix+numberOfCities 是指向第一个数据行的指针,但它仍然是 long int**。然后将其转换为long int*,然后通过i*numberOfCities 元素将其进一步移动(每个元素都是long int)。

      3) 您可以将内存分配为单个数组并执行 2D->1D 索引计算。或者您可以分配指针数组,然后为每一行分配一个数组。或者你可以分配指针数组,然后为所有数据分配数组,然后将指针设置到数据数组中的相应项,使每个指针都指向它的行。

      【讨论】:

        【解决方案6】:

        matrix[i] = (long int*) (matrix + numberOfCities) + i * numberOfCities;

        就像你自己说的 - 他们为数据和指针分配内存,数据将是连续的。但是分配数据是不够的,因为指针没有初始化(指向垃圾),所以现在他们必须将指针设置为实际指向数据。

        重要的是要了解他们所做的是首先您拥有所有指针,然后才是数据,例如:[ptr1,ptr2,ptr3,data1[],data2[],data3[]]

        现在让我们看看第一个指针 - 他应该指向数组的开头,所以如果 i=0 则我们得到 matrix[0] = (long int*)(matrix + numberOfCities); 指向数据部分的开头。

        对于i=1,我们得到matrix[0] = (long int*)(matrix + numberOfCities) numberOfCities;,它在数据部分开始之后恰好是“一个numberOfCities 块”。

        【讨论】:

          猜你喜欢
          • 2021-02-03
          • 2015-09-17
          • 1970-01-01
          • 1970-01-01
          • 2021-01-26
          • 2015-02-10
          • 2012-02-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多