在C中释放内存时应该考虑哪些主要问题?

时间:2010-12-04 17:26:07

标签: c memory-management memory-leaks

我试图找到一些明确解释的教程,在释放内存时需要记住的是什么。但是我找不到这样的东西。任何人都可以让我知道程序员在释放C中的内存时应该记住的主要内容。我目前正在处理链表。有些情况下新的链表使用2个或更多现有链接列表创建。例如:

 list l1;
 list l2 
 list l3 = list_append(l1,l2)
 list l4 = list_append(l3,l1)
 list l5 = list_append(l3,l4)

释放内存时我必须遵循的解除分配顺序是什么?

这里list_append是返回列表副本的函数。

6 个答案:

答案 0 :(得分:3)

使用malloc / free系列函数时,有两条规则需要遵守。

  1. 你只能free malloc家庭分配器返回的有效内存,释放它会使其无效(因此双重释放是一个错误,因为释放的内存不是从malloc获得的)。
  2. 在释放内存后访问内存是错误的。
  3. 以下是重要部分:分配器提供 no 工具来帮助您遵守这些规则

    你必须自己管理它。这意味着您必须安排程序的逻辑,以确保始终遵循这些规则。这是c,你肩负着大量乏味而复杂的责任。

    让我建议一些相当安全的模式:

    在同一上下文中分配和释放

    //...
    {
      SomeData *p = malloc(sizeof SomeData);
      if (!p) { /* handle failure to allocate */ }
      // Initialize p
    
      // use p various ways
    
      // free any blocks allocated and assigned to members of p
      free p;
    }
    //...
    

    在这里你知道数据p指向的是一次分配并释放一次,只在两者之间使用。如果初始化和释放SomeData的内容非常重要,那么你应该将它们包装成几个函数,这样就可以减少到

    //...
    {
      SomeData *p = NewSomeData(i,f,"name"/*,...*/); // this handles initialization
      if (!p) { /* handle failure to allocate */ }
    
      // use p various ways
    
    
      ReleaseSomeData(p) // this handles freeing any blocks 
                         // allocated and assigned to members of p
    }
    //...
    

    将此称为“范围所有权”。您会注意到它与本地自动变量没有太大区别,并且只提供了一些自动无法使用的选项 变量

    调用第二个选项“Structure Ownership”:此处删除已分配块的责任将移交给更大的结构:

    List L = NewList();
    //...
    
    while (something) {
      // ...
      Node n= NewNode(nodename);
      if (!n) { /* handle failure to allocate */ }
      ListAdd(L,n);               //  <=== Here the list takes ownership of the 
                                  // node and you should only access the node 
                                  // through the list.
      n = NULL; // This is not a memory leak because L knows where the new block is, and
                // deleting the knowledge outside of L prevents you from breaking the
                // "access only through the structure" constraint.
      //...
    }
    
    // Later
    RemoveListNode(L,key); // <== This routine manages the deletion of one node 
                           // found using key. This is why keeping a separate copy
                           // of n to access the node would have been bad (because
                           // your separate copy won't get notified that the block
                           // no longer valid).
    
    // much later
    ReleaseList(L); // <== Takes care of deleting all remaining nodes
    

    鉴于您有一个包含要添加和删除的节点的列表,您可能会考虑结构所有权模式,因此请记住:一旦节点提供给仅 的结构 em>通过结构访问它。

答案 1 :(得分:2)

这个问题一般没什么意义,唯一合理的答案似乎很明显:

  1. 内存是在第一个实例中动态分配的
  2. 在释放后,您不会再次尝试使用内存
  3. 你需要至少保留一个指向分配的指针,直到你需要释放它为止(即不要让你对该块的唯一引用超出范围或被销毁)。
  4. 第二个要求可以通过在解除分配后将指针设置为NULL或零来辅助,但指针可以保存在其他地方并不是万无一失。

    第三个要求在复杂数据结构中尤其是一个问题,其中分配的内存可能包含自身包含指向已分配内存的指针的结构。在释放更高级别的结构之前,您当然需要解除分配这些内容。

答案 2 :(得分:2)

  

任何人都可以告诉我这是什么   程序员的主要事情   在解除分配时应该记住   C中的记忆。

基本原则非常简单:使用* alloc系列函数分配的任何内存,包括malloccallocrealloc,都必须通过对{{{{}}的相应调用来解除分配。 1}}。

将指针(内存地址)传递给free()时,请记住,您可以传递给free()有效内存地址是以前返回的内存地址通过其中一个* alloc函数。一旦将内存地址传递给free(),该内存地址就不再有效,不能用于任何其他目的。

答案 3 :(得分:1)

第一个原则是:

  

无论你分配什么(使用calloc /   malloc)你最终需要自由。

在你的情况下,如果列表在每个追加上深度复制,我看不出有什么问题。您需要单独释放每个列表。

答案 4 :(得分:0)

使用valgrind(1)在您的特定情况下向您显示终止时仍存在的对象,然后修改您的代码以确保释放不必要的对象。

答案 5 :(得分:0)

最好的答案是一个问题..为什么你用C编写代码而不是使用更高级别的语言和更好的内存管理?