【问题标题】:Freeing pointers from inside other functions in C从 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 中的内存管理非常复杂(我希望不只是我)。任何人都可以了解正在发生的事情吗?

【问题讨论】:

    标签: c pointers memory-management


    【解决方案1】:

    是的,这将正确释放内存。

    pointer里面的清理函数是一个局部变量;为该函数在本地存储的传入值的副本。

    这可能会增加您的困惑,但您可以从 cleanup 方法内部调整变量 p(它是 mycode 方法的本地变量)的值,如下所示:

    void cleanup(MyType** pointer) {
      free(*pointer);
      *pointer = NULL;
    }
    

    在这种情况下,pointer 存储指针的地址。通过取消引用,您可以更改存储在该地址的值。你会像这样调用cleanup 方法:

    cleanup(&p);
    

    (也就是说,您要传递指针的地址,而不是其值的副本。)

    我会注意到,在软件的同一逻辑“级别”上处理分配和解除分配通常是一种好习惯 - 即不要让调用者负责分配内存然后在函数内部释放它。保持一致并处于同一水平。

    【讨论】:

    • 你没说这里的电话需要cleanup(&p);
    【解决方案2】:

    这不起作用,因为cleanup() 中的pointer 是本地的,因此调用函数看不到分配它NULL。有两种常见的方法来解决这个问题。

    1. 不是发送清除指针,而是发送一个指向指针的指针。因此将cleanup()更改如下:
    void cleanup(MyType** pointer)
    {
      free(*pointer);
      *pointer = NULL;
    }
    

    然后拨打cleanup(&p)

    1. 第二种很常见的选择是使用#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()函数的指针的本地副本,而不是原始指针。

      解决方法有以下三种:

      1. 使用指向指针的指针。

        void cleanup(struct MyType **p) { free(*p); *p = NULL; }
        
      2. 使用宏。

        #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)
        
      3. 将设置指向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函数吗?

          【讨论】:

            猜你喜欢
            • 2022-06-14
            • 1970-01-01
            • 1970-01-01
            • 2013-10-20
            • 2013-12-30
            • 2015-03-04
            • 2022-09-27
            • 1970-01-01
            • 2013-08-19
            相关资源
            最近更新 更多