C何时分配和释放内存 - 在函数调用之前,在函数调用之后......等等

时间:2010-06-08 14:22:40

标签: c memory-management

我正在使用我的第一个直接C项目,自从我在C ++上工作以来已经有一段时间了。所以整个内存管理有点模糊。

我创建了一个可以验证输入的函数。在下面的简单示例中,它只是忽略空格:

int validate_input(const char *input_line, char** out_value){

    int ret_val = 0; /*false*/
    int length = strlen(input_line);
    out_value =(char*) malloc(sizeof(char) * length + 1);

    if (0 != length){

        int number_found = 0;
        for (int x = 0; x < length; x++){

            if (input_line[x] != ' '){ /*ignore space*/

                /*get the character*/
                out_value[number_found] = input_line[x];
                number_found++; /*increment counter*/
            }
        }
        out_value[number_found + 1] = '\0';

        ret_val = 1;
    }

    return ret_val;

}

不是在 out_value 的函数内部分配内存,我应该在调用函数之前执行此操作并始终期望调用者在传入函数之前分配内存吗?根据经验,如果在函数返回之前总是释放在函数内部分配的任何内存吗?

8 个答案:

答案 0 :(得分:7)

我遵循两条非常简单的规则,让我的生活更轻松。

1 /只要您知道需要,就可以在需要时分配内存。这将允许您在做太多工作之前捕获内存不足错误。

2 /每个已分配的内存块都有一个责任属性。当责任通过函数接口时应该很清楚,此时释放该内存的责任随内存一起传递。这将保证某人具有明确规定的释放内存的要求。

在您的特定情况下,如果您希望将值返回给调用者,则需要传入一个双字符指针:

int validate_input (const char *input_line, char **out_value_ptr) {
    : :
    *out_value_ptr =(char*) malloc(length + 1); // sizeof(char) is always 1
    : :
    (*out_value_ptr)[number_found] = input_line[x];
    : :

只要您清楚地说明函数的期望,您就可以在调用者或函数本身中分配内存。因为你知道所需的尺寸,所以我更喜欢这个功能。

但请记住,您可以同时使用这两个选项。换句话说,如果函数传递指向NULL的char**,则让它分配内存。否则它可以假设调用者已经这样做了:

    if (*out_value_ptr == NULL)
        *out_value_ptr =(char*) malloc(length + 1);

答案 1 :(得分:2)

在上面的示例中,您应该在函数返回之前释放该内存。根据经验,您可以在结束定义变量的范围之前释放/删除已分配的内存。在您的情况下,范围是您的功能,因此您需要在功能结束之前释放它。如果不这样做,将导致内存泄露。

至于你的另一个问题,我认为它应该被分配到函数中,因为我们希望能够在函数之外使用它。你分配一些内存,你调用你的功能,然后你释放你的记忆。如果您尝试将其混合在函数中进行分配的地方,并且在外部进行释放会让人感到困惑。

答案 2 :(得分:2)

分配内存的函数/模块/对象是否应该释放它的想法在某种程度上是一个设计决定。在你的例子中,我(这里的个人意见)认为它对于分配它的功能是有效的,并让它由呼叫者释放。它使它更有用。

如果这样做,则需要以不同的方式声明输出参数(作为C ++样式的引用或C样式的char **。如定义的那样,指针将仅存在于本地并且将被泄露。

答案 3 :(得分:1)

典型的做法是在out_value外部分配内存,并用八位字节将块的长度传递给带指针的函数。这允许用户决定他们想要如何分配该内存。

此模式的一个示例是套接字中使用的recv函数:

 ssize_t recv(int socket, void *buffer, size_t length, int flags);

答案 4 :(得分:1)

以下是分配内存的一些指导原则:

  1. 仅在必要时分配。
  2. 巨大的物体应该是动态的 分配。大多数实现 没有足够的本地存储空间 (堆栈,全局/程序存储器)。
  3. 设置所有权规则 分配对象。老板应该是 负责删除。
  4. 解除内存释放指南:

    1. 删除如果已分配,请勿删除 对象或变量不是 动态分配。
    2. 不再使用时删除。 查看对象所有权规则。
    3. 程序退出前删除。

答案 5 :(得分:0)

在这个例子中,你不应该为out_value释放或分配内存。它被输入为char*。因此,您无法将新内存“返回”给函数的调用者。为此,您需要接受char**

在此特定情况下,缓冲区长度在调用者进行调用之前是未知的。此外,进行两次相同的调用将产生不同的值,因为您正在处理用户输入。因此,一旦获得长度并且使用分配的缓冲区第二次调用,就不能采用调用方法。因此,最好的方法是让函数分配内存并将释放的责任传递给调用者。

答案 6 :(得分:0)

首先,您提供的代码示例是 ANSI C.它看起来更像C ++。没有“&lt;&lt;” C中的运算符,作为“cout”的输出流。

下一个问题是如果你在这个函数中没有free(),你会泄漏内存。您传入char *但是一旦将该值赋给malloc()的返回值(避免在C编程语言中转换malloc()的返回值),该变量就不再指向任何值传入函数的内存地址。如果要实现该功能,将指针传递给char指针char **,您可以将其视为在C ++中通过引用传递指针(如果您想在C中使用那种语言,我不会“T)。

接下来,关于是否应该在函数调用之前或之后分配/释放取决于函数的角色。您可能有一个函数,它的工作是分配和初始化一些数据,然后将其返回给调用者,在这种情况下,它应该malloc(),调用者应该free()。但是,如果您只是使用几个缓冲区进行某些处理,则可能倾向于更喜欢调用者来分配和释放。但是对于你的情况,因为你的“validate_input”函数看起来只是复制没有空格的字符串,你可以只在函数中malloc()并将它留给调用者。虽然,因为在这个函数中,你只需要分配与整个输入字符串相同的大小,所以看起来好像你也可以拥有所有它的调用者。这一切都取决于你的用法。

请确保您不会像在此示例中那样丢失指针

答案 7 :(得分:0)

需要考虑的一些粗略指导原则:

  1. 首选让调用者分配内存。这使它可以控制分配内存的方式/位置。直接在代码中调用malloc()意味着您的函数正在指示内存策略。
  2. 如果无法预先知道可能需要多少内存,则您的功能可能需要处理分配。
  3. 如果您的函数确实需要分配,请考虑让调用者传入它使用的分配器回调,而不是直接调用malloc。这使得您的函数可以在需要时根据需要进行分配,但让调用者可以控制内存的分配方式和位置。