在c语言中释放二维矩阵时正确使用free()函数的方法

4

我刚开始学习C语言编程,关于2D矩阵与free()命令的结合,我有几个问题。

我知道首先需要创建一个指针数组,指向矩阵的不同列:

double **array = (double **)malloc(5*sizeof(double *));
for(int n = 0; n<5; n++){

array[n] = (double *) malloc(6*sizeof(double));

我知道正确的释放此矩阵的方法是首先释放单个行,然后再释放数组指针本身。大致如下:
for (i = 0; i < nX; i++){  
   free(array[i]); }     

free(array);

我的问题是:这为什么是必须的?我知道这种方法是不正确的,但为什么不能只使用 free(array) ?这会释放指针 array 的内存,据我所知,由列使用的内存不被其他程序访问时就会被覆盖。 free(array) 会导致内存损坏吗?
任何帮助都将不胜感激!

1
它不是一个二维数组。它是一个包含指向其他数组的指针的数组。所有这些其他数组都存在于内存中的其他位置,并且需要被释放。 - Christian Gibbons
@ChristianGibbons:这是指向指针的指针,而不是指向数组的指针。 - too honest for this site
1
@toohonestforthissite 我想保持清晰,这些不是单一的对象。以一种既技术正确又清晰的方式描述可能很困难。 - Christian Gibbons
不释放行不会导致内存损坏,只是意味着该内存在程序的生命周期内永远无法再次使用(因为它已被分配但未被释放,但您不知道它在哪里)。如果您经常这样做,将会耗尽内存。 - M.M
4个回答

6
这不会导致损坏,但会造成内存泄漏。如果在程序中只执行一次,可能并没有太大关系(许多专业/昂贵的应用程序都有 - 小型、无意的 - 内存泄漏),但如果在循环中重复执行,则可能会在一段时间后耗尽内存。 如果您的代码来自外部程序(如果您的代码在库中),也是如此。
另外:不释放缓冲区可能是一种有用的方法(暂时)检查程序中崩溃是否源于损坏的内存分配或释放(当您无法使用Valgrind时)。 但最终您需要释放所有内容,只需释放一次即可。
如果您想执行仅一次malloc,则还可以分配一个大块,然后计算行的地址。 最后,只需释放大块(示例在这里:如何使用一个malloc语句分配二维数组

6
你的代码不仅为指针数组(蓝色数组)分配内存,而且在for循环中,你还为红色数组分配了内存。因此,仅有free(array)这一行只会释放由蓝色数组分配的内存,而不是红色数组。你需要在失去与它们的联系之前释放红色数组;也就是说,在释放蓝色数组之前释放它们。
另外,顺便提一下:
“当其他东西需要访问列使用的内存时,该内存不会被覆盖吗?”
不会。操作系统会跟踪你的进程(程序)分配的内存,并且在你的进程终止之前不允许任何其他进程访问已分配的内存。在正常情况下,我是指记住C语言没有垃圾回收器,操作系统永远不知道你已经失去了与分配的内存空间的联系,也永远不会尝试像“好的,这个内存空间对于这个进程不再有用了;所以,让我们将其解除分配并为另一个进程服务。”这样的操作。

enter image description here


2
这是必要的,因为 C 语言没有垃圾回收器。一旦你使用 malloc 或类似的函数分配了内存,该内存将被标记为“正在使用”,直到程序运行结束。
即使你在程序中不再持有该内存的指针也无所谓。C 语言没有机制来检查这一点并自动释放内存。
此外,当你使用 malloc 分配内存时,函数并不知道你将用于什么目的。对于分配器来说,它只是一些字节而已。
因此,当你释放一个指针(或指针数组)时,没有逻辑去“意识到”这些是包含内存地址的指针。
这就是 C 语言的设计方式:动态内存管理几乎完全是手动的 - 留给程序员,因此你必须为每次调用 malloc 调用 free
1 C 语言确实处理了一些更繁琐的任务,例如找到可以获得所请求大小的连续空闲内存块。

0

让我们来看一个简单的例子:

int **ptr = malloc(2*sizeof *ptr);
int *foo = malloc(sizeof *foo);
int *bar = malloc(sizeof *bar);
ptr[0] = foo;
ptr[1] = bar;
free(ptr);

如果您的建议被实现,foobar将成为悬挂指针。如果您只想释放ptr,您将如何解决这种情况?

这是一个反事实的例子。 - Tim Randall
如果您的建议被实施了, - klutt
我可以读懂(甚至理解)。我的反对意见仍然存在,foobar都不是悬空指针,在原始代码中也没有悬空指针,因为在**array对象指向的**对象被释放后,它就不存在了。顺便说一下:悬空指针是指向未分配内存的指针。 - too honest for this site
@toohonestforthissite OP的建议是,调用free(ptr)时也应该释放指针ptr[0]ptr[1]。这将使foobar成为悬空指针。 - klutt
@Broman:啊,现在我明白他的意思了。然而,如果 OP 的建议得到实施,唯一合理的事情就是将 foobar 标记为空指针(或者“指向无处”)。到目前为止,OP 的问题没有得到很好的研究和思考。话虽如此,在有指向对象的指针的情况下长时间进行 GC 是没有意义的。但是 OP 明确展示了一个例子,在这个例子中,没有进一步的指向已经分配的对象,使得这个回答毫无意义。 - too honest for this site

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接