OS X 上的 malloc 问题

发布于 2024-09-28 09:35:06 字数 1090 浏览 5 评论 0原文

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

如果我将内存分配给像这样的 2D 数组this:

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 的工作原理?否则有人可以建议发生什么事吗?

亚历克斯

I have written a some C code running on OS X 10.6, which happens to be slow so I am using valgrind to check for memory leaks etc. One of the things I have noticed whilst doing this:

If I allocate memory to a 2D array like this:

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

Then check the memory address of matrix it is 0x0.

However if I do

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

This works fine, i.e. the pointer to the newly created memory is returned.

When I also have a free2D function to free up the memory. It doesn't seem to free properly. I.e. the pointer still point to same address as before call to free, not 0x0 (which I thought might be default).

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

My question is: Am I misunderstanding how malloc/free work? Otherwise can someone suggest whats going on?

Alex

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

下雨或天晴 2024-10-05 09:35:07

当你释放一个指针时,指针的值不会改变,如果你想让它为空,你必须显式地将它设置为0。

When you free a pointer, the value of the pointer does not change, you will have to explicitly set it to 0 if you want it to be null.

梦初启 2024-10-05 09:35:07

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

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

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

但我认为大多数人一看到 *** 就会尖叫。

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

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

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

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

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

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

,然后您可以通过一次取消引用和极快的乘以常数来将元素 (i,j) 作为矩阵 [i][j] 访问。如果您的矩阵仅在局部作用域或结构内部需要,只需将其声明为:

double matrix[4][4];

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

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

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

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

In the first example, you've only stored the pointer returned by malloc in a local variable. It's lost when the function returns.

Usual practice in the C language is to use the function's return value to pass the pointer to an allocated object back to the caller. As Armen pointed out, you can also pass a pointer to where the function should store its output:

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

but I think most people would scream as soon as they see ***.

You might also consider that arrays of pointers are not an efficient implementation of matrices. Allocating each row separately contributes to memory fragmentation, malloc overhead (because each allocation involves some bookkeeping, not to mention the extra pointers you have to store), and cache misses. And each access to an element of the matrix involves 2 pointer dereferences rather than just one, which can introduce stalls. Finally, you have a lot more work to do allocating the matrix, since you have to check for failure of each malloc and cleanup everything you've already done if any of them fail.

A better approach is to use a one-dimensional array:

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

then access element (i,j) as matrix[i*ncols+j]. The potential disadvantages are the multiplication (which is slow on ancient cpus but fast on modern ones) and the syntax.

A still-better approach is not to seek excess generality. Most matrix code on SO is not for advanced numerical mathematics where arbitrary matrix sizes might be needed, but for 3d gaming where 2x2, 3x3, and 4x4 are the only matrix sizes of any practical use. If that's the case, try something like

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

and then you can access element (i,j) as matrix[i][j] with a single dereference and an extremely fast multiply-by-constant. And if your matrix is only needed at local scope or inside a structure, just declare it as:

double matrix[4][4];

If you're not extremely adept with the C type system and the declarations above, it might be best to just wrap all your matrices in structs anyway:

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

Then declarations, pointer casts, allocations, etc. become a lot more familiar. The only disadvantage is that you need to do something like matrix.x[i][j] or matrix->x[i][j] (depending on whether matrix is a struct of pointer to struct) instead of matrix[i][j].

Edit: I did think of one useful property of implementing your matrices as arrays of row pointers - it makes permutation of rows a trivial operation. If your algorithms need to perform a lot of row permutation, this may be beneficial. Note that the benefit will not be much for small matrices, though, and than column permutation cannot be optimized this way.

ま昔日黯然 2024-10-05 09:35:07

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

Allocate2D(double**& matrix...)

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

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

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

稍后调用

Allocate2D(&matrix ...)

In C++ You should pass the pointer by reference :)

Allocate2D(double**& matrix...)

As to what's going on - well you have a pointer that is NULL, you pass the copy of that pointer to the function which allocated mamory and initializes the copy of your pointer with the address of the newly allocated memory, but your original pointer remains NULL. As for free you don't need to pass by reference since only the value of the pointer is relevant. HTH

Since there are no references in C, you can pass by pointer, that is

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

and later call like

Allocate2D(&matrix ...)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文