【问题标题】:Freeing dynamically allocated arrays in C inside second function在第二个函数内释放 C 中动态分配的数组
【发布时间】:2021-01-18 02:06:25
【问题描述】:

我的问题是关于在不同功能中释放分配的内存。所以我的代码结构如下:

int main()
{
    // Declare variables
    double *val1, *val2;

    // Call function 1
    function1(&val);

    // Call function 2
    function2(&val2);

    // Do some stuff .....

    // Free dynamically allocated memory
    free(val1);
    free(val2);

    // End program
    return 0;
}

void function1(double *val1)
{
    /* Allocate memory */
    val1 = (double*) malloc(n1*sizeof(double));
    if (val1 == NULL){
        printf("Error: Memory not allocated!");
        exit(0);
    }
}

void function2(double *val2)
{
    // Allocate memory
    val2  = (double*) malloc(n2*sizeof(double));
    if (val2== NULL){
        printf("Error: Memory not allocated!");
        // Here I want to free val1!
        exit(0);
    }
}

意味着在function1中为val1分配了一些内存,在function2中为val2分配了一些内存。

现在,function2 不需要 val1 中包含的内容,所以我乍一看不必将指针传递给 val1。

但是,如果 val2 没有正确分配,我想退出程序,但首先释放所有分配的内存。我可以在 function2 中释放 val1 的内存而不传递 val1 的指针吗?

【问题讨论】:

  • “我可以在函数 2 中释放 val1 的内存而不传递 val1 的指针吗”。你会如何想象它的工作原理?当然,您可以通过例如使用全局变量来破解它。但这显然是不可取的。正常的做法是让调用代码处理这种错误检查和恢复。
  • 另外,你的函数参数是错误的。这些参数必须是double **,如果你想将分配的指针传回给调用者,分配需要是*val1 = malloc(..);
  • 指向指针的指针
  • 请注意function1function2 首先是错误的。 val1 内部的 function1localfunction1。对val1 的更改不会修改mainval1。相同形式function2.
  • 如果 malloc 失败,你的整个堆无论如何都是烤面包。那时的清理工作有点重要,执行中的任何东西都不太可能被挽救。

标签: c function dynamic-memory-allocation


【解决方案1】:

使用动态分配构造函数的一种方法是返回整数值,例如:int function1(double *val) 这样,如果失败,您可以返回一个值,该值将在分配失败时指示它,并在 main()

中采取相应措施

【讨论】:

    【解决方案2】:

    我可以在 function2 中释放 val1 的内存而不传递 val1 的指针吗?

    没有。 C语言没有析构函数的概念,所以在其他语言中常用。在 C 语言中,您必须自己“捡拾垃圾”——因此,如果您终止程序,最好释放所有已分配的内存。错误处理有很多种风格,选择你喜欢的。我喜欢kernel coding style。一个在程序失败时终止程序的函数将是非常残酷的。让函数的用户处理错误情况的返回值会更好,更可预测。 C 函数通常(对我来说)返回 int0 表示成功,负值表示失败。您的程序可能如下所示:

    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int function1(double **val1)
    {
        size_t n1 = 10;
        *val1 = (double*) malloc(n1 * sizeof(**val1));
        if (*val1 == NULL){
           return -ENOMEM;
        }
        return 0;
    }
    
    int function2(double **val2)
    {
        size_t n2 = 20;
        *val2  = malloc(n2 * sizeof(double));
        if (*val2== NULL){
            return -ENOMEM;
        }
        return 0;
    }
    
    int main()
    {
        int err = 0;
    
        double *val1, *val2;
    
        err = function1(&val1);
        if (err) goto ERROR_function1;
    
        err = function2(&val2);
        if (err) goto ERROR_function2;
    
        err = do_some_calc(val1, val2);
    
        free(val2);
    ERROR_function2:
        free(val1);
    ERROR_function1:
        return err;
    }
    

    注意程序中的错误 - 您传递了 double** 指针,但您的函数需要 double* 指针。参数按值传递给函数 - 参数的值被复制。要修改一个值,你必须传递一个指针——包括指针,所以如果你想修改一个指针,你必须传递一个指向指针的指针。

    【讨论】:

      【解决方案3】:

      您可以通过以下方式编写更简洁的代码并修复一些错误:

      这是一个人为的例子,但你可以这样做:

      double function1 (void)
      {
        return malloc(n1*sizeof(double));
      }
      
      double function2 (void)
      {
        return malloc(n2*sizeof(double));
      }
      
      void exit_cleanup (double* v1, double* v2)
      {
        free(v1);
        free(v2);
        exit(0);
      }
      
      int main (void)
      {
        double* val1 = NULL;
        double* val2 = NULL;
      
        // ...
      
        val1 = function1();
        if(val1 == NULL) { exit_cleanup(&v1, &v2); }
          
        // ...
          
        val2 = function2();
        if(val2 == NULL) { exit_cleanup(&v1, &v2); }
      
        // Do some stuff .....
      
          
        exit_cleanup(&v1, &v2);
      }
      

      这是可行的,因为两个指针都被初始化为 NULL 并且调用 free(NULL) 是一个安全的空操作。所有错误处理和清理都集中在一个功能上。不需要“on error goto”的东西。

      另一个很少使用的野兽是atexit,它是一个标准的 C 函数,它允许您注册许多将在程序终止之前执行的函数。在这里可以正常工作 - 但是,您需要为这种情况设置变量文件范围,所以它并不理想。

      【讨论】:

        【解决方案4】:

        您要么必须将 val1 指针传递给 function2,要么将 val1 全局存储,这两种方法都很脏。

        解决此问题的正确方法是从这两个函数返回错误代码并处理来自main 的错误(即释放任何需要释放的已分配内存)。

        附带说明,&amp;val1 的类型为 double **,而不是 double *。即使在这里可能工作得很好,但它是不正确的,并且可能导致意外行为。您的功能需要像这样更改:

        void function1(double **val1)
        {
            /* Allocate memory */
            *val1 = malloc(n1*sizeof(double));
            if (*val1 == NULL){
                printf("Error: Memory not allocated!");
                exit(0);
            }
        }
        

        这将正确地保持数据类型的指针深度,并提前通知您任何指针问题。

        【讨论】:

        • 不好的做法。它应该是*val1 =malloc(n1*sizeof(**val1));
        • @P__J__ 同意这一点。只是忽略了那部分并从问题代码 sn-p 中获取。通过此更改更新答案
        • @P__J__ 这是为什么这种风格被高估的完美例子。因为很容易滑倒写malloc(n1*sizeof(*val1));。我也倾向于使用您提出的样式,但它并不是普遍适用的。另一种选择是malloc( sizeof(double[n1]) );,这很好,因为它是自记录代码。我们要分配一个数组,所以让我们分配我们想要的数组大小。
        • @Lundin 首先应该避免使用双指针参数的 void 函数。 double *function1(size_t n1) { return malloc( double[n1]);}
        • @P__J__ 是的,但许多专业 API 为属于该 API 的所有函数保留错误代码的返回值。那么你别无选择,只能通过其中一个参数指向指针。
        猜你喜欢
        • 2018-09-01
        • 2013-01-27
        • 1970-01-01
        • 2021-01-26
        • 1970-01-01
        • 2010-12-05
        • 2022-01-15
        • 2012-11-06
        • 1970-01-01
        相关资源
        最近更新 更多