双重递归中的 Malloc 和 free

Malloc and free in double recursion

当我遇到以下问题时,我正在玩 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)?

关于:

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()那个参数

解决方案是从 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[=37=中释放列表] 本身,因为我们稍后需要它。所以我们做下一个最好的事情 - 在调用 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() 中通过引用传递列表.