我正在努力想出一种干净的方法来处理C中分配的内存。假设我有这样的东西:
void function(int arg) {
char *foo;
foo = (char *)malloc(sizeof(char) * 100);
int i = func1(arg, &foo);
char *bar;
bar = (char *)malloc(sizeof(char) * 100);
int j = func2(&bar);
free(foo);
free(bar);
}
我的问题是func1
和func2
可能会遇到错误和exit(1)
,所以当这种情况发生时,我需要释放foo
和bar
。
如果func1
遇到错误,我只需要调用free(foo)
就可以了。但如果func2
遇到错误,我不能只调用free(bar)
,因为我还需要释放foo
。这可能会变得非常复杂,我觉得这不是处理记忆的正确方法。
我是不是遗漏了什么?如果有人能给我指明正确的方向,那就太棒了。谢谢
如果函数调用exit
,则根本不必清理内存使用情况,操作系统将释放它。但是,如果您需要释放其他资源(例如,锁定文件、清理临时文件…),那么您可以使用atexit
函数,或者如果您使用gnu-libc,则使用on_exit
函数来完成这项工作。
如果func1()
或func2()
在某种情况下要调用exit(1)
,则不必担心为foo
或bar
释放内存,因为一旦进程退出,操作系统通常会进行清理。
只要在正常执行过程中在正确的时间(在退出function
之前)free
,就不会出现内存泄漏。
我认为有一种简单的方法可以解决这个问题。
在分配资源时,只需在整个程序中维护一个allocCode,如下所示。有几个关键点需要记住。首先,不要在switch案例中使用break
语句。每成功分配一次资源,就增加allocCode。对于添加的每一个资源,您都应该在顶部的交换机中添加一个case,并使用一个更高的数字。因此,调用函数freeResourceBeforeExit()
将按正确的顺序释放所有资源。请记住,由于没有中断,开关箱将在正确的位置进入,并释放其入口点以下的所有资源。
我将编写psuedo代码。
int allocCode = 0;
int freeResourceBeforeExit()
{
switch(allocCode)
{
case 4:
free(resource3);
case 3:
free(resource2);
case 2:
free(resource1);
case 1:
free(resource0);
}
exit(0);
}
int main()
{
...
resource0 = malloc(10);
allocCode++;
func1();
...
resource1 = malloc(100);
allocCode++;
func2();
...
resource2 = malloc(1000);
allocCode++;
...
func3();
...
resource3 = malloc(10000);
allocCode++;
func4();
...
so on..
}
希望这能有所帮助!
如果将工作分为几个部分,那么管理资源会容易得多。
void part1(int arg) {
char *foo;
foo = (char *)malloc(sizeof(char) * 100);
int i = func1(arg, &foo);
free(foo);
}
void part2(void) {
char *bar;
bar = (char *)malloc(sizeof(char) * 100);
int j = func2(&bar);
free(bar);
}
void function(int arg) {
part1(arg);
part2();
}
现在,如果需要,每个部件都可以在退出前free
其参数。
原则上,可以安装一个具有atexit
的处理程序,该处理程序知道如何释放缓冲区。由于func1
调用exit
,将调用该处理程序。使用起来不太愉快——处理程序不带参数,这意味着您需要使用全局变量(或局部静态变量)来存储需要释放的东西。它不能被注销,这意味着你需要将这些全局设置为null(或其他一些特殊值),以表明你自己已经释放了资源,但处理程序仍然会被调用。通常,您会使用atexit
处理程序作为挂起自己的资源清理框架的"钩子"。
在实践中,对于一些malloc
ed缓冲区来说,这通常太麻烦了,因为当程序退出时,功能齐全的操作系统无论如何都会释放进程保留的所有内存。
在退出之前释放内存甚至可能代价高昂——为了用free
释放每个分配,内存将被触摸,这意味着它需要从主内存甚至从交换中拖到缓存中。对于大量可能需要一段时间的小额拨款。当操作系统为你做这件事时,它只是为进程取消映射内存映射,并开始在未来将该地址空间/内存/交换空间重新用于其他事情。清理有好处(例如,它使代码更容易重复使用,使真正的泄漏更容易发现),但也有成本。
顺便说一句,函数func1
在出错时调用exit
是相当反社会的,因为正如您所发现的,它限制了函数的用户。即使他们认为他们的程序可以/应该在func1
失败的情况下继续进行,他们也无法恢复。func1
实际上已经宣布,这对该项目来说太重要了,甚至不能梦想在没有结果的情况下继续下去。是的,GMP,我指的是你。
处理它的一种方法:
void function() {
int size = 100;
char bar[size];
char foo[size];
// do stuff
//compiler frees bar and foo for you
}