OS X 上的 malloc 问题

Posted

技术标签:

【中文标题】OS X 上的 malloc 问题【英文标题】:Problems with malloc on OS X 【发布时间】:2011-04-26 07:01:49 【问题描述】:

我已经编写了一些在 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函数来释放内存时。它似乎没有正确释放。 IE。指针仍然指向与调用 free 之前相同的地址,而不是 0x0(我认为这可能是默认值)。

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

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

亚历克斯

【问题讨论】:

是的,您误解了免费的工作原理。 Free 将p 指向的存储返回到“空闲列表”,但不会改变p,它现在指向一个实际内存地址,但该内存不再“属于”您的应用程序。以及 suihock 所说的。 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

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

void Allocate2D(double*** pMatrix...)

   *pMatrix = malloc(...)

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

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

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

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

然后以matrix[i*ncols+j] 访问元素(i,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]matrix-&gt;x[i][j] 之类的操作(取决于matrix 是否是指向结构的指针结构)而不是matrix[i][j]

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

【讨论】:

谢谢。我现在在 valgrind 无内存泄漏的帮助下接受了关于 n 的建议。耶:)【参考方案3】:

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

Allocate2D(double**& matrix...)

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

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

Allocate2D(double*** pMatrix...)

   *pMatrix = malloc(...)

然后再打电话

Allocate2D(&matrix ...)

【讨论】:

是的,我想通过引用和值传递。只是想不出解决办法。谢谢。 亲爱的投反对票的人,您能评论一下您投反对票的原因吗? @Paul R:如果你没有注意到,我也提供了 C 答案 @Armen:为什么还要对 C 问题给出 C++ 答案?如果您对 C 的了解不够,无法知道它没有引用,那么您可能应该将自己限制在 C++ 问题上。 +1 这太不可思议了,这是唯一正确的答案(因为 OP 的主要问题是参数传递)并且他被否决了!这里的人怎么了?他同时给出 C 和 C++ 答案是一个奖励。

以上是关于OS X 上的 malloc 问题的主要内容,如果未能解决你的问题,请参考以下文章

如果 Malloc 无法分配 X 字节,我是使用 Realloc 还是再次使用 Malloc? C++

malloc分配内存为多大?

嵌入式系统上的 malloc 行为

c上的代码崩溃,很可能与malloc有关?

malloc和数组有啥区别

在 C++ 中,结构上的“new/delete”代替“malloc/free”有多好?