【问题标题】:Malloc and free in double recursion双重递归中的 malloc 和 free
【发布时间】:2020-05-14 15:53:20
【问题描述】:

当我遇到以下问题时,我正在使用 C 代码。我有一个看起来像这样的代码:

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

typedef struct a {
  int n1;
  int n2;
} el;

typedef struct list {
  el *elements;
  int nElements;
} list;

void copyList(list in, list *out) {
    int i;
    for(i = 0; i < in.nElements; i++) {
        out->elements = realloc(out->elements,sizeof(el)*(out->nElements + 1));
        out->elements[i] = in.elements[i];
        out->nElements = out->nElements + 1;
    }
}

void initList(list *l) {
    l->nElements = 0;
    l->elements = NULL;
}

void freeList(list *l) {
    l->nElements = 0;
    if(l->elements != NULL) {
        free(l->elements);
        l->elements = NULL;
    }
}

void testRecur(int i, list l) {
    list l1;

    if(i > 0) {
        initList(&l1);
        copyList(l,&l1);
        // freeList(&l);
        testRecur(i-1,l1);
        testRecur(i-2,l1);
        // freeList(&l1);
    }
    else {
        // freeList(&l);
    }

}

int main(void) {
  list l1;
  list l2;
  el element;

  initList(&l1);
  initList(&l2);

  l1.elements = malloc(sizeof(el)*2);
  l1.nElements = 2;

  element.n1 = 1;
  element.n2 = 2;
  l1.elements[0] = element;

  element.n1 = 3;
  element.n2 = 4;
  l1.elements[1] = element;

  testRecur(10,l1);

  // freeList(&l1);

  return 0;
}

这是一个简单的代码,递归地复制一个列表一定次数。它没有任何意义,但很容易解释这个概念。问题是:我应该在哪里拨打free 电话?我已经尝试了注释点的所有组合,但我最终遇到了双重释放错误(*** Error in ./a.out: double free or corruption (fasttop): 0x0000000001ecd010 ***)或valgrind 的内存泄漏检测(使用以下参数:valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes ./a.out)。有没有办法不泄漏内存(至少根据 valgrind)?

【问题讨论】:

  • free(&amp;l1); --> free(l1.elements);
  • 释放与分配malloc 结果相同的东西。
  • 关于:out-&gt;elements = realloc(out-&gt;elements,sizeof(el)*(out-&gt;nElements + 1)); 调用 realloc() 时可能会失败。将返回的指针保存在“临时”指针检查 (!=NULL) 中,并且仅当不是 NULL 时,将该指针分配给目标变量。 out-&gt;elements 如果 ==NULL,则调用 perror( "realloc failed" ); 注意一旦 realloc() 失败,它将(可能)继续失败。所以建议“清理”并致电exit( EXIT_FAILURE );

标签: c recursion malloc free


【解决方案1】:

解决方案是从 testRecur() 函数中删除所有出现的 freeList(&l);

说明:

使用 alloc(及其变体)和 free 的一个好习惯是尝试在相同的范围内执行这两种操作。在您的示例中,分配内存到列表的函数在 main()copyList() 中。

1.main()函数分配和解除分配可以在同一范围内轻松完成:

int main(void) {
  /*
   Initialization code
  */
  // Allocation
  l1.elements = malloc(sizeof(el)*2);
  l1.nElements = 2;
  /*
   Process list l1, but dont play with its memory.
   A thumb rule can be to pass l1(by reference) as const
  */

  // De-allocation
  freeList(&l1);

  return 0;
}

2.copyList():根据代码,我们不能在 copyList 本身中释放列表,因为我们以后需要它。所以我们做下一件最好的事情——在调用 copyList 的函数中取消分配。想想 copyList 有两个部分 - 分配和复制。

void testRecur(int i, list l) {
    list l1;

    if(i > 0) {
        initList(&l1);
        /* memory will be allocated in copyList. 
           So, allocation scope starts here */
        copyList(l, &l1);
        /* Process list l1, but again, dont play with its memory */
        testRecur(i-1,l1);
        testRecur(i-2,l1);
        /* Now l1 is no more required, its scope is finished.
           We can free l1. */
        freeList(&l1);
    }
}

注意:这与分配和释放无关,但在您的示例中将结构(列表)作为值传递没有意义,因此最好在函数testRecur()中通过引用传递列表。

【讨论】:

    【解决方案2】:

    关于:

    void testRecur(int i, list l) {
        list l1;
    
        if(i > 0) {
            initList(&l1);
            copyList(l,&l1);
    

    copyList() 的调用将调用realloc() 的结果放在局部变量l1 中但是,那些realloc()d 内存永远不会传递给free()

    建议:

    void testRecur(int i, list l) 
    {
        list l1;
    
        if( i > 0 )
        {
            initList(&l1);
            copyList(l,&l1);
            testRecur(i-1,l);
            freeList(&l1);
        }
    }
    

    然后,局部变量分配的内存将在递归展开堆栈时被释放。

    但是,这将导致函数的第二个参数永远不会被释放。所以main() 函数仍然需要free() 那个参数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-15
      • 2018-05-01
      • 2021-12-11
      • 1970-01-01
      • 2012-06-10
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      相关资源
      最近更新 更多