在C中动态分配2D数组

时间:2013-11-03 22:39:03

标签: c pointers memory-management multidimensional-array

首先,我不是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中的2D数组进行内存分配,或者这是正确的方法?

6 个答案:

答案 0 :(得分:5)

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

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

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

没有复杂的指针指针方案指针,只需要一个连续对象的单个分配。然后,您可以像A[i][j]一样访问该矩阵的元素,并且编译器正在为您执行所有索引计算。

所以无论何时你(你没有遗留代码强加指针指针,你有一个支持C99的编译器)你应该使用它。它更简单,更不容易出错,效率更高。

答案 1 :(得分:2)

鉴于sizeof (long int) = ln个城市和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. 整个2D数组可以在一个free调用中被删除(当然,程序员释放代码时应该知道一个free调用就是他所有的问题/她必须制作)。

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

答案 2 :(得分:1)

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的一个块”。

答案 3 :(得分:1)

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

答案:

1)是的。

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

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

答案 4 :(得分:1)

创建2D数组的一种简单方法是:

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;
}

答案 5 :(得分:0)

这是动态分配2D数组的另一种方法

#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维数组并超越二维限制。