从 C 中的其他函数内部释放指针
Posted
技术标签:
【中文标题】从 C 中的其他函数内部释放指针【英文标题】:Freeing pointers from inside other functions in C 【发布时间】:2011-08-28 14:19:50 【问题描述】:考虑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 中的内存管理非常复杂(我希望不只是我)。任何人都可以了解正在发生的事情吗?
【问题讨论】:
【参考方案1】:是的,这将正确释放内存。
pointer
里面的清理函数是一个局部变量;为该函数在本地存储的传入值的副本。
这可能会增加您的困惑,但您可以从 cleanup
方法内部调整变量 p
(它是 mycode
方法的本地变量)的值,如下所示:
void cleanup(MyType** pointer)
free(*pointer);
*pointer = NULL;
在这种情况下,pointer
存储指针的地址。通过取消引用,您可以更改存储在该地址的值。你会像这样调用cleanup
方法:
cleanup(&p);
(也就是说,您要传递指针的地址,而不是其值的副本。)
我会注意到,在软件的同一逻辑“级别”上处理分配和解除分配通常是一种好习惯 - 即不要让调用者负责分配内存然后在函数内部释放它。保持一致并处于同一水平。
【讨论】:
你没说这里的电话需要cleanup(&p);
【参考方案2】:
这不起作用,因为cleanup()
中的pointer
是本地的,因此调用函数看不到分配它NULL
。有两种常见的方法来解决这个问题。
-
不是发送清除指针,而是发送一个指向指针的指针。因此将
cleanup()
更改如下:
void cleanup(MyType** pointer) free(*pointer); *pointer = NULL;
然后拨打cleanup(&p)
。
-
第二种很常见的选择是使用
#define
宏来释放内存并清除指针。
如果您使用的是 C++,那么还有第三种方法,将 cleanup()
定义为:
void cleanup(MyType& *pointer) // 你的旧代码保持不变
【讨论】:
【参考方案3】:是的
是的
是的:malloc
(3) 神奇地产生了一块内存。您已将此内存的地址分配给指针p
,但没有以任何有意义的方式分配内存本身,它是mycode()
中的auto
变量。
然后,您将p
按值传递给cleanup()
,这将复制指针,并使用cleanup()
的本地副本释放块。 cleanup()
然后将它自己的指针实例设置为 NULL,但这没用。函数完成后,参数pointer
将不复存在。
回到mycode()
,您仍然有指针p
持有地址,但该块现在位于空闲列表中,在再次分配之前对存储没有太大用处。
您可能会注意到,您甚至仍然可以存储到*p,
并从中读取,但是会发生各种下游丢失,因为这块内存现在属于库,您可能会损坏它的数据结构或malloc() 块的未来所有者。
仔细阅读 C 可以让您对变量生命周期有一个抽象的概念,但是将参数传递和局部变量分配的近乎通用(无论如何对于编译语言)实现可视化为堆栈操作要容易得多。在 C 课程之前学习汇编课程会有所帮助。
【讨论】:
嘿嘿嘿...不,我是认真的。实际上我开始了一个很长的回复,但后来我决定放弃它,因为它更像是关于教学编程如何从自下而上(IMO 的最佳方式)转变为自上而下(效果不佳,主要是因为那里is no top) 到充值(即从 Java 之类的丑陋事物开始,无处可去)。我真的相信指针非常简单,但前提是您牢牢掌握计算机的工作原理(简单的组装是 IMO 一个很好的起点)。没有这种基础编程,就只是一堆具有奇怪属性的神奇单词。 @6502:我完全同意——C64 的“用户指南”很棒。 @6502,当然,好点。但我“得到”的是你的用户名。不错的选择。【参考方案4】:不只是你。
cleanup()
将正确清理您的分配,但不会设置指向 NULL
的指针(恕我直言,这应该与清理分开。) 指针指向的数据 被传递通过指针到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
的指针的责任留给调用者。这可以避免不必要的分配和代码混乱或损坏。
【讨论】:
【参考方案5】:这里有两个问题:
我认为之后是不是错了 清理(p);被称为,内容 p 现在应该为 NULL 吗?
是的,这是错误的。调用free
后,指针指向的内存被释放。这并不意味着指针指向的内容被设置为 NULL。此外,如果您希望指针 p
在 mycode
中变为 NULL,则不会发生,因为您将 p
的 copy 传递给 cleanup
。如果您希望p
在mycode
中为NULL,那么您需要一个指向cleanup
中的指针的指针,即清理签名将是cleanup(MyType**)
。
第二个问题:
是否会正确清理(MyType* 指针) 释放内存分配?
是的,因为您正在对malloc
返回的指针执行free
,所以内存将被释放。
【讨论】:
【参考方案6】:cleanup
将正确释放p
,但不会更改其值。 C 是一种按值传递的语言,因此您不能从被调用函数更改调用者的变量。如果您想从cleanup
设置p
,您需要执行以下操作:
void cleanup(MyType **pointer)
free(*pointer);
*pointer = NULL;
然后这样称呼它:
cleanup(&p);
你的代码有点不习惯,你能解释一下你为什么要写这个cleanup
函数吗?
【讨论】:
以上是关于从 C 中的其他函数内部释放指针的主要内容,如果未能解决你的问题,请参考以下文章