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

这是我的代码:

#include  #include  #include  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和用户)。
我该如何解决?

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

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

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

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

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

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

    不要做:

     ptr = realloc(ptr, newsize); 

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

    使用:

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

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

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

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

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

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

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

 #include  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进行免费使用!

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

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

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

同时getinfo()看起来很痛苦。 尝试像fgets(mybuffer,len_max,stdin)这样的东西。

最后我查了一下,可执行文件甚至没有打扰到最后“释放”所有不同的块,它只是走开了。 VM系统将所有使用的内存(包括堆栈和程序代码)返回给操作系统,然后进程蒸发并结束。 malloc()ed块只是该内存上的一个字节模式,而且它们都被遗忘了。

这不仅仅是一个普通的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; } 

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