2

首先,我不是 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;
}

我发现代码有点奇怪,因为我期待与此更相似的东西对我来说似乎更清楚。无论如何,我想知道以下几点:

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

6 回答 6

5

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

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

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

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

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

于 2013-11-04T00:06:54.003 回答
2

鉴于sizeof (long int) = ln城市和,这段代码试图做与您在问题中链接到的文章sizeof (long int *) = p几乎相同的事情。只是,它尝试通过一次调用而不是多次调用来做到这一点。malloc

分配的第一个np字节用于保存n指针。这些指针中的每一个都指向一大块nl长整数。当然,这些long int块都是相邻的,第一个块本身与np 大小的指针的块相邻,并且从地址 开始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. 可能具有缓存优势。阵列在一个块中,空间局部性发挥作用。另一方面,多个malloc调用可能会返回彼此远离的块。

于 2013-11-03T23:11:50.423 回答
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 块”。

于 2013-11-03T22:49:24.747 回答
1

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

答案:

1) 是的。

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

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

于 2013-11-03T22:50:10.847 回答
1

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

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;
}
于 2013-11-03T22:58:28.780 回答
0

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

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

于 2013-11-03T22:48:43.417 回答