这是我的代码:
#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)。
我该如何解决?
答案 0 :(得分:3)
如前所述,如果您要退出,则所有实用的现代操作系统将在退出之前释放已分配的内存。并非总是如此; AmigaDOS的早期版本IIRC在重启之前没有自动回收分配的内存。
这是一个简单的案例。有更复杂的情况,例如您正在将文件解析到内存中并且579 th 内存分配失败,并且您希望释放先前的578内存分配,以便用户可以再次尝试。
在这种情况下,您必须记录每个相关的内存分配(这可能本身需要一些内存分配 - 但如果您正在解析文件,则可能有一个包含完整描述的主结构)然后释放所有已分配的数据。
在您的示例中,如果这不是main()
函数,并且如果您没有中止内存分配错误,那么您需要确保在退出函数时释放三个已分配的指针。标准技巧包括:
将指针初始化为0,以便可以可靠地释放它们:
char *host = 0;
char *user = 0;
使用realloc()
时,请勿将结果分配给作为第一个参数传递的表达式:
不要这样做:
ptr = realloc(ptr, newsize);
如果(when)ptr
是对分配的内存的唯一引用并且重新分配失败,那么你只是泄漏了内存;没有办法释放仍然分配给你的内存。
使用:
void *newptr = realloc(oldptr, newsize);
if (newptr == 0)
...recover from memory allocation failure
oldptr = newptr;
更简单版本的问题在于您只是抛弃了对分配内存的唯一引用。 (请注意,您的代码属于危险/泄漏模式)。
请注意,几乎每个获取资源的函数都必须在返回之前释放获取的资源,或者让资源可用于程序的其他部分,以便其他部分可以在完成时释放资源。它
'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
,这将释放s2
和s1
。最后,如果一切顺利,我们会做任何事情,然后,所有三个指针都将被释放。如果这是一个普通的函数,我们可能会返回一个指针,所以在“结束”位之前会有一个单独的返回值,而不是“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)
在退出之前,您不必释放动态分配的内存。操作系统将使所有内存可用于下一个过程。