C:正确释放多维数组的内存

Posted

技术标签:

【中文标题】C:正确释放多维数组的内存【英文标题】:C: Correctly freeing memory of a multi-dimensional array 【发布时间】:2010-12-16 13:46:41 【问题描述】:

假设您有以下初始化多维数组的 ANSI C 代码:

int main()

      int i, m = 5, n = 20;
      int **a = malloc(m * sizeof(int *));

      //Initialize the arrays
      for (i = 0; i < m; i++)  
          a[i]=malloc(n * sizeof(int));
      

      //...do something with arrays

      //How do I free the **a ?

      return 0;

使用**a后,如何正确从内存中释放它?


[更新](解决方案)

感谢 Tim(和其他人)answer,我现在可以执行这样的功能来从我的多维数组中释放内存:

void freeArray(int **a, int m) 
    int i;
    for (i = 0; i < m; ++i) 
        free(a[i]);
    
    free(a);

【问题讨论】:

术语狡辩:这不是 C 通常所说的“多维数组”。这只是使用语法a[i][j] 的唯一方法,同时仍然允许在编译时两个维度都是未知的。另一种多维数组是数组的数组,而不是指向数组(的第一个元素)的指针数组。 【参考方案1】:

好的,有相当多的混乱解释到底是什么顺序 必要的free() 电话必须在,所以我会尝试澄清什么 人们正在尝试了解以及为什么。

从基础开始,释放已分配的内存 使用malloc(),您只需使用指针调用free() malloc() 给你的。所以对于这段代码:

int **a = malloc(m * sizeof(int *));

你需要一个匹配的:

free(a);

对于这一行:

a[i]=malloc(n * sizeof(int));

你需要一个匹配的:

free(a[i]);

在一个类似的循环中。

这变得复杂的地方在于它需要发生的顺序。如果 您多次致电malloc() 以获得几个不同的块 记忆,一般来说,你打电话给free()的顺序并不重要 你已经完成了他们。但是,顺序在这里很重要 具体原因:你正在使用一大块malloced 内存来保存 指向malloced 内存的其他块的指针。因为你必须 不要在你交还内存后尝试读取或写入内存 free(),这意味着您将不得不使用 他们的指针存储在a[i] 之前 你释放a 块本身。 带有存储在a[i] 中的指针的各个块不依赖于每个块 其他,所以可以是freed,按照你喜欢的顺序。

所以,把这一切放在一起,我们得到了这个:

for (i = 0; i < m; i++)  
  free(a[i]);

free(a);

最后一个提示:调用 malloc() 时,请考虑更改这些:

int **a = malloc(m * sizeof(int *));

a[i]=malloc(n * sizeof(int));

到:

int **a = malloc(m * sizeof(*a));

a[i]=malloc(n * sizeof(*(a[i])));

这是做什么的?编译器知道a 是一个int **,所以它可以 确定sizeof(*a)sizeof(int *) 相同。然而,如果 稍后你改变主意并想要chars 或shorts 或longs 或 数组中的任何内容而不是ints,或者您修改此代码以供以后使用 用在别的东西上,你只需要改变剩下的 在上面引用的第一行中引用int,以及其他所有内容 会自动为您到位。这消除了可能性 未来未被注意到的错误。

祝你好运!

【讨论】:

+1 很好的答案;感谢您解释关于“逆序”的问题以及关于做sizeof(*a) 的要点 另外,如果我错了,请纠正我,但我是否正确地说sizeof(*a[i]) 等同于您的sizeof(*(a[i])),因为数组符号[] 的优先级高于@ 987654356@? 不,我认为你是对的。 en.wikipedia.org/wiki/… 但是,我根据经验法则工作,如果我必须查找它,可能其他阅读我的代码的人也必须查找它,所以为了节省他们(以及我,稍后)的麻烦,明确地使用括号有助于澄清事情并节省时间。不过,这只是个人喜好。【参考方案2】:

完全撤消您分配的内容:

  for (i = 0; i < m; i++)  
      free(a[i]);
  
  free(a);

请注意,您必须按照您最初分配内存的相反顺序执行此操作。如果您先执行free(a),那么a[i] 将在内存被释放后访问内存,这是未定义的行为。

【讨论】:

说你必须以相反的顺序释放可能会产生误导。你只需要在指针本身之后释放指针数组。 这不是反过来的另一种说法吗? 我认为@Andomar 的意思是你释放 a[i] 的顺序无关紧要,只是你必须在释放 a 之前释放所有它们。换句话说,您可以释放 a[0] 到 a[m-1] 或 a[m-1] 到 a[0] 或所有偶数 a[] 后跟赔率。但我也确定@GregH 并不是意味着您必须以相反的顺序执行 a[],尤其是考虑到他的代码。【参考方案3】:

您需要再次迭代数组并为指向的内存执行与 malloc 一样多的释放,然后释放指针数组。

for (i = 0; i < m; i++)  
      free (a[i]);

free (a);

【讨论】:

【参考方案4】:

以完全相反的顺序编写分配运算符,更改函数名称,一切都会好起来的。

  //Free the arrays
  for (i = m-1; i >= 0; i--)  
      free(a[i]);
  

  free(a);

当然,您不必必须以完全相同的相反顺序解除分配。您只需要准确地跟踪一次释放相同的内存,而不是“忘记”指向已分配内存的指针(就像您首先释放 a 一样)。但是以相反的顺序解除分配对于解决后者来说是一个很好的经验。

正如 cmets 中的 litb 所指出的,如果分配/解除分配有副作用(如 C++ 中的new/delete 运算符),有时解除分配的向后顺序会比这个特定示例中更重要.

【讨论】:

为什么一定要在循环中颠倒顺序? 因为a[1]是在a[0]之后分配的,所以你应该先deallocate a[1]。 为什么需要在 a[0] 之前释放 a[1] ?它们是不同的 malloced 块,它们之间没有依赖关系 由于free 在 C 语言中没有副作用(关于您自己的程序代码),因此这似乎并不重要。 “逆向操作”规则对于具有附加到 freealloc 的副作用的语言更为重要,例如带有析构函数/构造函数的 C++。 我很确定大多数人会向前而不是向后释放数组。【参考方案5】:

我只会调用 malloc() 和 free() 一次:

#include <stdlib.h>
#include <stdio.h> 

int main(void)
  int i, m = 5, n = 20;
  int **a = malloc( m*(sizeof(int*) + n*sizeof(int)) );

  //Initialize the arrays
  for( a[0]=(int*)a+m, i=1; i<m; i++ ) a[i]=a[i-1]+n;

  //...do something with arrays

  //How do I free the **a ?
  free(a);

  return 0;

【讨论】:

如何回答这个问题? Pavel Shved 写出了正确答案。我只是用一些代码写了一个评论。 您应该将 cmets 写入问题的“评论”字段。它还支持代码块。 @litb:请将我的答案复制到问题的评论字段中。谢谢。 没有理由投反对票。我发现许多关于 SO 的问题的答案是“你不应该那样做,而是这样做”。有回答问题的字母和回答问题含义的空间。

以上是关于C:正确释放多维数组的内存的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp Ç与C ++的动态多维数组的建立与释放

“动态分配的内存模拟多维数组”的正确术语?

正确分配多维数组

多维数组如何存储在内存中? [复制]

C中的多维数组:它们是锯齿状的吗?

C语言多维数组与多级指针