2

我已经编写了一些在 OS X 10.6 上运行的 C 代码,它恰好很慢,所以我使用 valgrind 来检查内存泄漏等。我在执行此操作时注意到的一件事:

如果我将内存分配给这样的二维数组:

double** matrix = NULL;
allocate2D(matrix, 2, 2);

void allocate2D(double** matrix, int nrows, int ncols) {
    matrix = (double**)malloc(nrows*sizeof(double*));
    int i;
    for(i=0;i<nrows;i++) {
        matrix[i] = (double*)malloc(ncols*sizeof(double));
    }
}

然后检查矩阵的内存地址是0x0。

但是,如果我这样做

double** matrix = allocate2D(2,2);

double** allocate2D(int nrows, int ncols) {
    double** matrix = (double**)malloc(nrows*sizeof(double*));
    int i;
    for(i=0;i<nrows;i++) {
        matrix[i] = (double*)malloc(ncols*sizeof(double));
    }
return matrix;
}

这很好用,即返回指向新创建内存的指针。

当我还有一个free2D函数来释放内存。它似乎没有正确释放。即指针仍然指向与调用free 之前相同的地址,而不是0x0(我认为这可能是默认值)。

void free2D(double** matrix, int nrows) {
    int i;
    for(i=0;i<nrows;i++) {
        free(matrix[i]);
    }
free(matrix);
}

我的问题是:我是否误解了 malloc/free 的工作方式?否则有人可以建议发生了什么?

亚历克斯

4

3 回答 3

4

当您释放指针时,指针的值不会改变,如果您希望它为空,则必须将其显式设置为 0。

于 2010-10-16T10:05:45.830 回答
3

在第一个示例中,您只将返回的指针存储malloc在局部变量中。当函数返回时它会丢失。

C 语言中通常的做法是使用函数的返回值将指向已分配对象的指针传回给调用者。正如 Armen 指出的,您还可以将指针传递给函数应存储其输出的位置:

void Allocate2D(double*** pMatrix...)
{
   *pMatrix = malloc(...)
}

但我想大多数人一看到就会尖叫***

您可能还认为指针数组不是矩阵的有效实现。分别分配每一行会导致内存碎片、malloc开销(因为每次分配都涉及一些簿记,更不用说您必须存储的额外指针)和缓存未命中。并且对矩阵元素的每次访问都涉及 2 个指针取消引用,而不仅仅是一个,这可能会引入停顿。最后,您还有很多工作要做分配矩阵,因为您必须检查每一个是否失败,malloc如果其中任何一个失败,您必须清理您已经完成的所有工作。

更好的方法是使用一维数组:

double *matrix;
matrix = malloc(nrows*ncols*sizeof *matrix);

然后访问元素 (i,j) 作为matrix[i*ncols+j]。潜在的缺点是乘法(在古代 CPU 上很慢,但在现代 CPU 上很快)和语法。

一个更好的方法是不要寻求过度的普遍性。SO 上的大多数矩阵代码不适用于可能需要任意矩阵大小的高级数值数学,而是用于 3d 游戏,其中 2x2、3x3 和 4x4 是唯一可实际使用的矩阵大小。如果是这种情况,请尝试类似

double (*matrix)[4] = malloc(4*sizeof *matrix);

然后您可以使用matrix[i][j]单个取消引用和极快的乘以常数来访问元素 (i,j)。如果您matrix只需要在本地范围或结构内部,只需将其声明为:

double matrix[4][4];

如果您对 C 类型系统和上面的声明不是非常熟练,那么最好还是将所有矩阵包装在结构中:

struct matrix4x4 {
    double x[4][4];
};

然后声明、指针转换、分配等变得更加熟悉。唯一的缺点是您需要执行类似matrix.x[i][j]or matrix->x[i][j](取决于是否matrix是指向结构的指针的结构)而不是matrix[i][j].

编辑:我确实想到了将矩阵实现为行指针数组的一个有用属性——它使行的排列成为一项微不足道的操作。如果您的算法需要执行大量行排列,这可能是有益的。请注意,对于小矩阵来说,好处不会太多,而且列排列不能以这种方式优化。

于 2010-10-16T18:10:46.943 回答
1

在 C++ 中你应该通过引用传递指针:)

Allocate2D(double**& matrix...)

至于发生了什么-您有一个为NULL的指针,您将该指针的副本传递给分配内存的函数并使用新分配的内存的地址初始化指针的副本,但您的原始指针仍然为NULL . 至于免费,您不需要通过引用传递,因为只有指针的值是相关的。高温高压

由于C中没有引用,所以可以通过指针传递,即

Allocate2D(double*** pMatrix...)
{
   *pMatrix = malloc(...)
}

后来打电话给

Allocate2D(&matrix ...)
于 2010-10-16T10:00:22.160 回答