众所周知,如果我们按值将指针传递给函数,则无法在函数内部释放它,如下所示:
void func(int *p)
{
free(p);
p = NULL;
}
p 持有一个(大概有效)地址的副本,所以 free(p) 试图释放它。但由于它是副本,因此无法真正释放它。对free()的调用如何知道它不能真正释放它?
上面的代码不会产生错误。这是否意味着free()只是静默地失败,"不知何故"知道作为参数传入的地址无法处理?
p持有一个(大概有效)地址的副本,所以 free(p) 试图释放它。但由于它是副本,因此无法真正释放它。
这不是真的。 如果p
是malloc()
(或NULL
)返回的有效地址,free()
可以正常工作。
事实上,这是实现自定义"析构函数"函数的常见模式(在用 C 语言编写 OO 样式代码时)。
您可能的意思是,在此之后p
不会更改为NULL
- 但这很自然,因为您是通过值传递它。如果要free()
并清空指针,则通过指针("byref")传递它:
void func(int **p)
{
if (p != NULL) {
free(*p);
*p = NULL;
}
}
并像这样使用它
int *p = someConstructor();
func(&p);
// here 'p' will actually be NULL
唯一的问题是这个函数是否在不同的DLL(Windows)中。然后,它可能与不同版本的标准库链接,并对如何构建堆有不同的想法。
否则没问题。
按值将p
传递给func()
,这将复制指针并创建本地副本,以释放内存func()
。 然后,func()
将自己的指针实例设置为p
NULL
但无用。函数完成后,参数p
结束存在。在调用函数中,您仍然有指针p
保存地址,但该块现在在空闲列表中,并且在再次分配之前对存储没有用处。
每个人都在说你的内存会被free(p);
释放,但你的原始指针(你用来调用函数)仍然会保存(现在无效的)地址。如果在比原始指针更晚的阶段分配了包含地址的新内存块,则会再次生效(对于内存管理器),但现在将指向完全不同的数据,从而导致各种问题和混乱。
你真的释放了内存块。在函数调用之后,传递给此函数的指针指向无处:相同的地址,但MMU不再知道如何处理此地址