c指针,如何将它/它们释放到函数中

时间:2012-05-05 17:15:54

标签: c function pointers

这是我的代码:

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

void getinfo(unsigned int a, unsigned int b, char **pStr);

int main(){
    unsigned int len_max = 8;
    unsigned int current_size = 0;
    current_size = len_max;
    char *host, *user;
    char *pStr = malloc(len_max);
    if(pStr == NULL){
        perror("\nMemory allocation\n");
        return EXIT_FAILURE;
    }
    printf("Inserisci hostname: ");
    getinfo(len_max, current_size, &pStr);
    if((host=malloc(strlen(pStr)+1 * sizeof(char))) == NULL) abort();
    strncpy(host, pStr, strlen(pStr)+1);
    printf("Inserisci username: ");
    getinfo(len_max, current_size, &pStr);
    if((user=malloc(strlen(pStr)+1 * sizeof(char))) == NULL) abort();
    strncpy(user, pStr, strlen(pStr)+1);
    printf("\nHostname: %s\nUsername: %s\n", host, user);
    free(pStr);
    free(host);
    free(user);
    return EXIT_SUCCESS;
}

void getinfo(unsigned int a, unsigned int b, char **pStr){
    unsigned int i = 0;
    int c = EOF;
    while((c = getchar()) != '\n'){
        (*pStr)[i++] = (char)c;
        if(i == b){
            b = i+a;
            if((*pStr = realloc(*pStr, b)) == NULL){
                perror("\nMemory allocation error\n");
                exit(EXIT_FAILURE);
            }
        }
    }
    (*pStr)[i]='\0';
}

问题是如果realloc失败,我必须退出(因为我无法分配内存)。但在退出之前,将释放所有使用过的指针。
问题是如果函数第一次失败,那么只有1个指针必须被释放(pStr)。
但如果它第二次失败则需要释放2个指针(pstr&amp; user)。
我该如何解决?

5 个答案:

答案 0 :(得分:3)

如前所述,如果您要退出,则所有实用的现代操作系统将在退出之前释放已分配的内存。并非总是如此; AmigaDOS的早期版本IIRC在重启之前没有自动回收分配的内存。

这是一个简单的案例。有更复杂的情况,例如您正在将文件解析到内存中并且579 th 内存分配失败,并且您希望释放先前的578内存分配,以便用户可以再次尝试。

在这种情况下,您必须记录每个相关的内存分配(这可能本身需要一些内存分配 - 但如果您正在解析文件,则可能有一个包含完整描述的主结构)然后释放所有已分配的数据。

在您的示例中,如果这不是main()函数,并且如果您没有中止内存分配错误,那么您需要确保在退出函数时释放三个已分配的指针。标准技巧包括:

  1. 将指针初始化为0,以便可以可靠地释放它们:

    char *host = 0;
    char *user = 0;
    
  2. 使用realloc()时,请勿将结果分配给作为第一个参数传递的表达式:

    不要这样做:

    ptr = realloc(ptr, newsize);
    

    如果(when)ptr是对分配的内存的唯一引用并且重新分配失败,那么你只是泄漏了内存;没有办法释放仍然分配给你的内存。

    使用:

    void *newptr = realloc(oldptr, newsize);
    if (newptr == 0)
        ...recover from memory allocation failure
    oldptr = newptr;
    

    更简单版本的问题在于您只是抛弃了对分配内存的唯一引用。 (请注意,您的代码属于危险/泄漏模式)。

  3. 请注意,几乎每个获取资源的函数都必须在返回之前释放获取的资源,或者让资源可用于程序的其他部分,以便其他部分可以在完成时释放资源。它

    'make available'操作可能会将获取的资源(将其视为内存,但可能是文件描述符,目录流或任何大量其他已分配的资源)返回给调用函数,或者将它存储在传递给当前函数的结构中,或将其复制到全局或(文件)静态变量,或者甚至将其存储在(函数)静态变量中,这样如果再次调用该函数,它就有一些资源可用进入。

答案 1 :(得分:3)

正如一些人所指出的,现代操作系统在退出时回收内存。但是,无论如何都要将资源释放为最佳实践,因为这样可以更轻松地进行调试。例如,如果您正在尝试查找泄漏并使用valgrind之类的工具,那么您没有正确释放的所有内存(即使通过程序逻辑,这也无关紧要)将显示为泄漏。有一些大型的API,众所周知不会这样做,并且它们会在使用它们的应用程序中进行跟踪泄漏。

此外,在某些特殊环境中,自行清理可能很重要。因此,现在进入是个好习惯。

你偶尔会看到的一种清理技术(例如,在Linux内核中)是我认为的“保释和释放”模式。它是少数(也许是唯一的)上下文之一,其中goto仍然被认为是可接受的。这取决于您是否能够按照分配它们的相反顺序释放资源。通常这是在单个函数的上下文中,在这种情况下main()

#include <stdlib.h>

int main(void) {
    int exit_status = EXIT_FAILURE;

    char *s1, *s2, *s3;

    s1 = malloc(100);
    if (!s1) return EXIT_FAILURE;

    s2 = malloc(100);
    if (!s2) goto endB;

    s3 = malloc(100);
    if (!s3) goto endA;

    exit_status = EXIT_SUCCESS;

    /* do whatever */

    free(s3);
endA:
    free(s2);
endB:
    free(s1);
    return exit_status;
}            

解释:如果分配s1失败,我们只是返回 - 没有什么可以清理。但是,如果分配s2失败,我们会转到endB,释放s1。如果分配s3失败,我们会转到endA,这将释放s2s1。最后,如果一切顺利,我们会做任何事情,然后,所有三个指针都将被释放。如果这是一个普通的函数,我们可能会返回一个指针,所以在“结束”位之前会有一个单独的返回值,而不是“return null”。

Nb:请不要将此作为免费使用goto的许可证!

答案 2 :(得分:3)

这更像是一般的C语言建议,而不是具体的答案,但是评论的时间太长了。

在存在动态资源管理的情况下编写C的常用方法是goto合适的标签,后面跟着相关的释放调用:

int f(int n)
{
    void * p1, * p2, * p3;
    int result = -1;

    p1 = malloc(n);

    if (!p1) goto END0;
    if (n % 2) goto END1;

    p2 = malloc(3 * n);

    if (!p2) goto END1;
    if (n % 7 == 0) goto END2;

    p3 = malloc(7 * n + 8);

    if (!p3) goto END2;

    if (do_useful_stuff(p1, p2, p3) == -1) goto END3;
    result = compute_more_stuff(p1, p2, p3);

END3:
    free(p3);
END2:
    free(p2);
END1:
    free(p1);
END0:
    return result;
}

另一种方法是将代码拆分成非常小的函数,一次只做一件事,并以临时方式处理资源分配:

int small_function(void ** p)
{
    void * q = malloc(13);

    if (some_init(&q) == -1)
    {
        free(q);    // ad-hoc exit management
        return -1;
    }

    int n = complex_computation(q);
    free(q);
    return n;
}

答案 3 :(得分:3)

是的,如果这是整个程序,你不必释放任何东西。

释放内存的全部原因是它可以在程序后面的某个地方重复使用。即使你的程序从这一点开始,你只分配了少量的字节。分配它们并永远保存它们是可以的。

事实上,你甚至不必做你在那里做算术来制作精确大小的mallocs,你可以说哦,用户名和主机名永远不会超过30个字符,只是为了确保我'll分配256个char块。哦等你的最大值是8个字符,无论如何。或者甚至只需要一个全局缓冲区256个字符或8个字符长。然后确保你的strncpy()s永远不会超过len_max,否则你就会冒着缓冲区溢出的风险。

同时getinfo()看起来很痛苦。尝试类似fgets(mybuffer,len_max,stdin)。

最后我检查过,可执行文件甚至没有费心去“释放”所有不同的块,它只是走开了。 VM系统将所有使用过的内存(包括堆栈和程序代码)返回给操作系统,进程蒸发并结束。

表示malloc()ed块只是该内存中的一个字节模式,而且它们都被遗忘了。

答案 4 :(得分:2)

在退出之前,您不必释放动态分配的内存。操作系统将使所有内存可用于下一个过程。