【问题标题】:C: Correctly freeing memory of a multi-dimensional arrayC:正确释放多维数组的内存
【发布时间】: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] 的唯一方法,同时仍然允许在编译时两个维度都是未知的。另一种多维数组是数组的数组,而不是指向数组(的第一个元素)的指针数组。

标签: c memory-management memory-leaks free malloc


【解决方案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 的问题的答案是“你不应该那样做,而是这样做”。有回答问题的字母和回答问题含义的空间。
    猜你喜欢
    • 2014-11-05
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 2014-10-03
    • 2015-05-10
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    相关资源
    最近更新 更多