释放C中的树结构

Freeing a tree structure in C

我在我的 C 程序中使用结构树,其中的结构设置如下:

typedef struct SymbolTable_t
{
    int id;

    Symbol symbols[size];
    int count;
    struct SymbolTable_t * parent;

    int scopeCount;
    struct SymbolTable_t * childScopes[size];

    int isM;
} SymbolTable;

当我创建一个新的 SymbolTable 时,我会像这样为子作用域分配内存:

SymbolTable *t = malloc(sizeof(*t));
// other stuff ...
for(int i = 0; i < size; i++)
{
   childScopes[i] = malloc(sizeof(SymbolTable)); // <---- line 41
}

在程序结束时,当我想释放分配给它们的内存时,我编写了一个递归函数:

void freeSymbolTables(SymbolTable* root)
{
    if(root == NULL) return;
    for(int i = 0; i < size; i++)
    {
        freeSymbolTables(root->childScopes[i]);
    }
    free(root);
}

请注意,我并没有真正使用整个 childScopes,因此只有其中一些会在程序结束时实际指向某些内容。我认为这很好但是 运行ning 在 valgrind 中它告诉我大量内存是 'definitely lost' 在第 41 行,我认为这意味着指针超出范围而没有调用 free程序生命周期结束。我不确定这里出了什么问题,因为我认为这应该释放所有内存。

最小可重现示例

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

#define s_size 40

typedef struct SymbolTable_t
{
    struct SymbolTable_t * s[s_size];
    int count;
} SymbolTable;

SymbolTable* createSymbolTable()
{
    SymbolTable* t = malloc(sizeof *t);
    t->count = 0;
    for(int i = 0; i < s_size; i++)
    {
        t->s[i] = malloc(sizeof t->s[i]);
    }
    return t;
}

void freeSymbolTable(SymbolTable* root)
{
    if(root == NULL) return;
    for(int i = 0; i < root->count; i++) freeSymbolTable(root->s[i]);
    for(int i = root->count; i < s_size; i++) free(root->s[i]);
    free(root);
}

void addChildScope(SymbolTable* parent, SymbolTable* child)
{
    parent->s[parent->count++] = child;
}

int main()
{
    SymbolTable *t =  createSymbolTable();
    SymbolTable *c = createSymbolTable();
    addChildScope(t, c);
    freeSymbolTable(t);
    return 0;
}

当我 运行 这样做时,valgrind 告诉我肯定丢失了 8 个字节,甚至认为我认为我的函数应该释放所有内存。

你基本上是在做:

// in createSymbolTable()
   t->s[0] = malloc(...);

然后:

// in addChildScope()
     parent->s[0] = child;      // malloc(...) from above is just lost...

您可以使用以下方法“修补”/解决它:

void addChildScope(SymbolTable* parent, SymbolTable* child) {
    free(parent->s[parent->count]);
    parent->s[parent->count++] = child;
}

但总的来说,如果内存从未被使用过,我认为在 createSymbolTable 中分配任何内容毫无意义。无论如何不要在 createSymbolTable 中分配它,内存不会被使用。

此外,理论上(但从未使用过,所以无所谓):

t->s[i] = malloc(sizeof t->s[i]);

可疑 - 应该是:

t->s[i] = malloc(sizeof *t->s[i]);