【问题标题】:How to correctly free a structure returned from a function?如何正确释放从函数返回的结构?
【发布时间】:2018-05-02 10:58:18
【问题描述】:

我是 C 的新手,并试图弄清楚如何处理从函数返回的结构,对这些结构的引用。

例如,这大致就是我想要做的。

typedef struct test_t{
    char *test_c;
} test_t;

int testFunc(test_t** output){
    test_t* testStruct = malloc(sizeof(testStruct));
    char* buf = malloc(sizeof(char)  * 5);
    strcpy(buf, "test");
    testStruct->test_c = buf;
    *output = testStruct;
    return 0;
}

int main() {
    test_t* test;
    testFunc(&test);
    printf("%s\n",test->test_c);
    free(test);
    return 0;
}

所以我主要得到了测试结构。在printf 之后(假设病房后有一些代码)我不再需要它并想释放它。但是如何正确地做到这一点?我应该先明确地释放 test_c 吗?但是如果没有分配呢?

【问题讨论】:

  • 在执行free(test); 之前,您只需执行free(test_c);。 “但如果没有分配呢?” - 如果test 没有分配怎么办?有一种通用的方法可以确定 test->test_c 是否“可释放”。你需要自己管理。
  • 另见(可能重复):does free() follow pointers?
  • 根据经验,分配代码是负责释放的代码。所以设计这样一个程序的正确方法是创建两个文件 test.h 和 test.c。在 test.c 中,一个函数“test_allocate”进行分配,一个函数“test_free”进行释放。调用者负责调用这两个函数,但不负责了解结构的内部工作原理。

标签: c c11


【解决方案1】:

一个结构可以像任何其他指针一样被释放。但是,您必须确保在这样做之前释放其所有分配的成员(如果不这样做很可能会导致结构成员的内存泄漏)。

typedef struct test_t{
    char *test_c;
} test_t;

int testFunc(test_t** output){
    test_t* testStruct = malloc(sizeof(testStruct));
    char* buf = malloc(sizeof(char)  * 5);
    strcpy(buf, "test");
    testStruct->test_c = buf;
    *output = testStruct;
    return 0;
}

int main() {
    test_t* test;
    testFunc(&test);
    printf("%s\n",test->test_c);
    free(test->test_c);
    free(test);
    return 0;
}

如果你的结构没有分配它的所有元素,你可以将它们的指针设置为NULL,一旦传递给free (3)就相当于什么都不做。

【讨论】:

  • 谢谢!我错过了关于将 NULL 分配给引用以防止尝试释放未分配指针的部分。现在很清楚了。
【解决方案2】:

但是如何正确地做到这一点呢?我应该先明确地释放 test_c 吗?但是如果没有分配呢?

基本经验法则:如果您编写一个分配资源的函数,请编写一个补充性的释放它的函数。因此,当应用于您的示例时:

void testFree(test_t* toFree) {
    free(toFree->test_c);
    free(toFree);
}

...然后...

printf("%s\n",test->test_c);
testFree(test);

如果testFreetestFunc 一起实现并公开,则调用者不需要知道您的实现细节。作为同一库的一部分的函数本身将确保正确维护不变量。如果您曾经切换分配方案,则无需更改调用代码。您只需修改 testFree 以配合新的 testFunc 并重新链接。

【讨论】:

    【解决方案3】:

    首先,你需要释放你为结构中的字符分配的内存,因为你已经初始化了它。否则,你会得到一个错误,所以在任何释放指令之前,你应该检查保存你试图释放的 test_p 内存位置的指针是否不是 NULL。此外,free 指令应该以 *test 作为参数,而不是 test。

    【讨论】:

      【解决方案4】:

      您应该有一个分配和初始化test_t 实例的过程。在 C++ 或其他面向对象的语言中,这将是一个构造函数。在 C 语言中,您必须自己滚动 - 类似于以下内容:

      test_t *create_test_t(char *init_test_c)
        {
        test_t *tt;
      
        tt = malloc(sizeof(test_t));
        if(init_test_c != NULL)
          {
          tt->test_c = malloc(strlen(init_test_c)+1);
          strcpy(tt->test_c, init_test_c);
          }
        else
          tt->test_c = NULL;
        }
      

      您还需要一个函数来正确删除test_t 实例。比如:

      void destroy_test_t(test_t *tt, bool static)
        {
        free(tt->test_c);
      
        if(!static)
          free(tt);
        }
      

      static 参数用于控制tt 是否应该被释放。显然,您不想尝试释放指向test_t 的静态实例的指针,在这种情况下,您会为static 传递TRUE。对于test_t 的动态分配实例,例如那些使用create_test_t 过程创建的,你会为static 传递FALSE。

      然后,您每次需要创建或销毁test_t 时都使用这些函数。

      祝你好运。

      【讨论】:

        【解决方案5】:

        我应该先明确地释放 test_c 吗?

        是的,你必须先释放test_c,因为如果你释放test,你将失去对test_c的引用。

        但是如果没有分配呢?

        当您从testFunc 返回int 时,您可以将其用作结构分配的状态(尽管恕我直言,返回指针会更好):

        int testFunc(test_t** output){
            test_t* testStruct = malloc(sizeof(testStruct));
            if (testStruct == NULL)
            /* testStruct allocation failure. */
                 return -1;
            char* buf = malloc(sizeof(char)  * 5);
            if (buf == NULL) {
            /* buf allocation failure. */
                 free(testStruct);
                 return -1;
            }
            strcpy(buf, "test");
            testStruct->test_c = buf;
            *output = testStruct;
            return 0;
        }
        

        之后,你检查testFunc失败:

        /* Check for allocation failure. */
        if (testFunc(&test) == -1) {
            fprintf(stderr, "Allocation error");
            exit(EXIT_FAILURE);
        }
        /* Allocation succeeded. */
        printf("%s\n",test->test_c);
        free(test->test_c);
        free(test)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-07
          • 2020-03-03
          • 2022-01-18
          • 2011-01-11
          相关资源
          最近更新 更多