结构破坏(释放内存)

时间:2019-11-14 15:16:20

标签: c free

每当为用户定义的结构编写析构函数时,您是否会尝试跳入该结构并尽可能地释放它,或者您只是释放该结构本身并期望调用者注意内存泄漏。 / p>

我可以想到两种方法的利弊。有没有一种标准的社区认可的编写析构函数的方法?

玩具示例

struct node {
    int *ptr;
    int num;
}
void node_free(struct node *n) {
    /* Would you include this? */
    if (n->ptr != NULL) free(n->ptr);
    free(n);
}

提出问题的场景

在图算法中,我希望能够一次将顶点结构插入到多个列表中。我创建了一个指向顶点的包装器结构,然后可以将这些包装器结构插入列表中。构造包装器时,我将指针传递给顶点结构。销毁包装器时,也无法销毁顶点结构。这就是让我问这个问题的场景:是否有一种标准的编写析构函数的方法可以让程序员不必担心这些细节?

3 个答案:

答案 0 :(得分:5)

拥有自定义结构的自定义析构函数的主要兴趣在于:确保同时释放结构的每个分配部分。应该将struct用户分配并开始使用它,然后在不再使用它时销毁它。

答案很简短:

答案 1 :(得分:4)

编辑以解决评论中的问题:
...您能否在答案顶部总结关于构造函数分配的内容与析构函数释放的内容之间的对称性的观点...

几个注释使构造函数和析构函数与对称引用相邻。任何编程语言都可以实现称为 Constructor & Destructor 的概念。但是,这些术语在面向对象的语言(例如C ++)的上下文中更常用,它们是类中的成员函数,这些类旨在创建,初始化和销毁​​它们所属的类中的对象和内存。此实现是面向对象编程所独有的。但是因为C不是面向对象的语言,所以构造函数/析构函数对的实现将有所不同。 C++'s language-enforced Constructor/Destructor symmetry 不是C的固有功能,尽管在C中有一些示例,其中对称形式被镜像资源获取/释放功能对,例如。 fopen / fclose中,对称性的实施方式不同。

尽管我在此处发布的答案中没有包含相同的 level 对称性(即,两个函数之间的参数列表并不相同。)存在某些对称性。 createTestStructfreeTestStruct函数的设计方式与 the answer here 相似,以确保使用createTestStruct创建的所有资源都将被释放和清理。通过致电freeTestStruct

原始答案
如果您具有struct *类型,并且该类型还具有成员指针,则最好使用可调用方法来完成运行时内存分配,并在提供该方法的同时向free提供另一个方法创建的所有内存。例如,如果您有这样的话:

typedef struct {
    size_t bufSize;
    char *buf;
}TEST;

调用函数拥有创建和销毁(或免费)创建内容的方法会容易得多。

例如,以下原型将很有用:

TEST * createTestStruct(int instances, size_t Bufsize);
void freeTestStruct(TEST *test, int instances);

用法示例:

int main(void)
{
    TEST *test = createTestStruct(10, 80);//creates instance of test, and pointer members 10 buf, each with space for 80 char

    // use test

    freeTestStruct(test, 10);//frees all 10 instances of test and buf
   return 0;
}

TEST * createTestStruct(int instances, size_t size)
{
    int i;
    TEST *test = calloc(instances, sizeof(*test));
    if(test)
    {
        for(i=0;i<instances;i++)
        {
            test[i].size = size;
            test[i].buf = calloc(test[i].size, 1);
        }
    }
    return test;
}

void freeTestStruct(TEST *test, int instances)
{
    int i;
    for(i=0; i<instances;i++)
    {
        free(test[i].buf);
    }
    free(test);
}

答案 2 :(得分:1)

我更喜欢不释放结构本身,仅释放内容的析构函数。这样一来,析构函数也可以用在不能释放的实例上,例如其他结构的字段,自动变量和数组元素。 如果我们只提供一个例程,该例程将实例和内容一起释放,那么用户如何清空例如自动变量?

例如,我们可以拥有

typedef struct { int n, nA; int* v; } ivT;
typedef struct { ivT is; ivT js; } egT;

并提供

ivT* ivEmpty( ivT* iv)
{ if ( iv) { free( iv->v); memset( iv, 0, sizeof *iv); }
  return iv;
}
egT* egEmpty( egT* eg)
{ if ( eg) 
  {  ivEmpty( &eg->is); ivEmpty( &eg->js);
     memset( eg, 0, sizeof *eg); 
  }  
  return eg;
}

例如,我们还可以提供一个释放结构本身的析构函数

egT* egFree( egT* eg)
{  return (free( egEmpty( eg)), NULL);
}