从 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。此外,如果您希望指针 pmycode 中变为 NULL,则不会发生,因为您将 pcopy 传递给 cleanup。如果您希望pmycode 中为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 中的其他函数内部释放指针的主要内容,如果未能解决你的问题,请参考以下文章

1-认识c指针

1-认识c指针

C++ - 未分配被释放的指针

C: 被释放的 malloc 错误指针未被分配

从函数返回一个指针,然后释放指针

在用free()函数释放指针内存时为何要将其指针置空