考虑c代码:
void mycode() {
MyType* p = malloc(sizeof(MyType));
/* set the values for p and do some stuff with it */
cleanup(p);
}
void cleanup(MyType* pointer) {
free(pointer);
pointer = NULL;
}
我错误地认为在调用cleanup(p);
之后,p的内容现在应该是NULL吗? cleanup(MyType* pointer)
会正确释放内存分配吗?
我正在编写我的大学作业,并发现调试器仍然显示指向内存地址而不是0x0(或NULL),如我所料。
我发现C中的内存管理非常复杂(我希望不仅仅是我)。能不能说明正在发生的事情?
答案 0 :(得分:18)
是的,这将正确释放内存。
清理函数中的 pointer
是局部变量;仅为该函数本地存储的值传递的副本。
这可能会增加您的困惑,但您可以从p
方法调整变量mycode
的值,如下所示:
void cleanup(MyType** pointer) {
free(*pointer);
*pointer = NULL;
}
在这种情况下,pointer
存储指针的地址。通过解除引用,您可以更改存储在该地址的值。
我将注意到,在软件的同一逻辑“级别”处理分配和释放通常是一种好习惯 - 即不要让调用者负责分配内存然后将其释放到函数内部。保持一致并处于同一水平。
答案 1 :(得分:10)
cleanup
将正确释放p
,但不会更改其值。 C是一种按值传递的语言,因此您无法从被调用函数更改调用者的变量。如果您想从p
设置cleanup
,则需要执行以下操作:
void cleanup(MyType **pointer) {
free(*pointer);
*pointer = NULL;
}
并称之为:
cleanup(&p);
您的代码有点不恰当,您能解释一下为什么要编写这个cleanup
函数吗?
答案 2 :(得分:7)
是强>
是强>
是: malloc
(3)神奇地产生了一块内存。您已将此内存的地址(而非内存本身)以任何有意义的方式分配给p
中auto
变量的指针mycode()
。
然后,您通过值将p
传递给cleanup()
,这将复制指针,并使用cleanup()
本地副本释放该块。 cleanup()
然后将它自己的指针实例设置为NULL,但这没用。函数完成后,参数pointer
就不复存在了。
回到mycode()
,您仍然有指针p
持有一个地址,但该块现在位于空闲列表中,对于存储非常有用,直到再次分配。
你可能会注意到你甚至可以存储到*p,
并从中回读,但是会发生各种下游丢失,因为这块内存现在属于库,你可能会损坏它的数据结构或者malloc()块的未来所有者的数据。
仔细阅读C可以给出一个关于变量生命周期的抽象概念,但是将参数传递和局部变量分配的近通用(对于编译语言,无论如何)的实现可视化为堆栈操作要容易得多。它有助于在C课程之前参加装配课程。
答案 3 :(得分:3)
这不起作用,因为pointer
中的cleanup()
是本地的,因此调用函数看不到它NULL
。有两种常见的方法可以解决这个问题。
cleanup()
如下:void cleanup(MyType** pointer) { free(*pointer); *pointer = NULL; }
然后只需致电cleanup(&p)
。
#define
宏来释放内存并清理指针。如果您使用的是C ++,那么第三种方法是将cleanup()
定义为:
void cleanup(MyType& *指针) { //你的旧代码保持不变 }
答案 4 :(得分:1)
这里有两个问题:
以后我想错了 清理(P);被称为,内容 p现在应该是NULL?
是的,这是错误的。在调用free
之后,指针指向的内存被释放。这并不意味着指针指向的内容设置为NULL。此外,如果您希望指针p
在mycode
中变为NULL,则不会发生这种情况,因为您将p
的 copy 传递给cleanup
}。如果您希望p
中的mycode
为NULL,则需要指向cleanup
中指针的指针,即清除签名为cleanup(MyType**)
。
第二个问题:
将正确清理(MyType *指针) 释放内存分配?
是的,因为你在free
返回的指针上执行malloc
,内存将被释放。
答案 5 :(得分:1)
不只是你。
cleanup()
将正确清理您的分配,但不会将指针设置为NULL
(应该将IMHO视为与清除分开。)指针指向的数据通过指针传递给cleanup()
,并且free()
正确编辑,但指针本身按值传递,因此当您将其设置为NULL
时你只影响cleanup()
函数的指针本地副本,而不是原始指针。
有三种方法:
使用指向指针的指针。
void cleanup(struct MyType **p) { free(*p); *p = NULL; }
使用宏。
#define cleanup(p) do { free(p); p = NULL; } while(0)
或(可能更好):
void cleanup_func(struct MyType *p) { /* more complicated cleanup */ }
#define cleanup(p) do { cleanup_func(p); p = NULL; } while(0)
负责将NULL
指针设置给调用者。这可以避免不必要的分配和代码混乱或破坏。