【问题标题】:Memory leaks in doubly linked list双向链表中的内存泄漏
【发布时间】:2019-03-06 05:03:13
【问题描述】:

我对 C 编程很陌生。

我有一个任务,我们应该在其中创建一个整数的双向链表,并编写一些函数来操作它们。我们被要求防止内存泄漏,但我不确定如何做到这一点。

在制作链表时,我必须malloc 很多次才能创建和存储节点,而且我很确定malloc 为节点提供足够的空间然后释放节点不是一个好主意在同一个地方指向它。

因此,我最好的猜测是我应该释放主函数中的所有节点,当我将它们的内容打印到屏幕上并且不再需要它们时。我尝试实现一个kill 函数,它将对列表中第一个节点的引用head 作为输入,并迭代这些节点,并在它们运行时释放它们。

我什至安装了 valgrind 来尝试查看是否有任何内存泄漏,看起来还有一些。我不知道他们来自哪里或如何解决这个问题。

这是整个代码:

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>

 typedef struct Node{
     int data;
     struct Node *next;
     struct Node *previous;
 }Node;

 void print_dll(Node *head){
     Node *curr = head;
     while(curr != NULL){
         printf("%d\t", curr->data);
         curr = curr->next;
     }
     puts(" ");
 }

Node* create_dll_from_array(int array [], int arrSize){
     //this is a function that creates a doubly linked list 
     //with the contents of the array
     Node* current = (Node *) malloc (sizeof(Node * ));
     current->data = array[arrSize-1];
     current -> next = NULL;

     for(int i = 2; i <= arrSize; i++){
         //create a new node
         Node * temp = (Node*)malloc(sizeof(Node*));
         //I would like the dll to be in the same order as the array, I        guess it isn't strictly necessary
         temp ->data = array[arrSize-i];
         temp -> next = current;
         current-> previous = temp;
         //now make temp the current
         current = temp;
     }
     current-> previous = NULL; 
     return current;
 }

  void insert_after(Node* head, int valueToInsertAfter, int valueToInsert ){
     if(head != NULL){
         Node * current = head;

         while(current-> data != valueToInsertAfter){
         //this while loop brings 'current' to the end of the list if
         //the searched value is not there
             if(current-> next != NULL){
                 current = current->next;
             }else{
                 break;
             }
         }
         //after exiting this loop, the current pointer is pointing
         //either to the last element of the dll or to the element 
         //we need to insert after

         Node *new = (Node *) malloc (sizeof(Node *));
         new->data = valueToInsert;
         new->next = current->next;
         new->previous = current;
         if(current->next != NULL){
             (current->next)->previous = new;
         }
         current->next = new;
     }
 }

 void delete_element(Node* head, int valueToBeDeleted){
     //work in progress
 }
 void kill(Node *head){
 //this is my attempt at freeing all the nodes in the doubly linked list
     Node *current;
     while(head!=NULL){
         current = head;
         head = head->next;
         free(head);
     }
 }
 int main(){
     int array [5] = {11, 2, 7, 22, 4};
     Node *head;

     /*Question 1*/
     //creates a doubly linked list from the array below
     head = create_dll_from_array(array, 5); ///size of the array is 5

     /* Question 2 */
    // print_dll(head);

     /*Question 3*/
     // to insert 13 after the first appearance of 7
     insert_after(head, 7, 13);
     print_dll(head);
     //to insert 29 after first appearance of 21
     insert_after(head, 21, 29);
     print_dll(head);

     /*Question 6*/
     //create a function to free the whole list

     kill(head);


     return 0;

 }

这里的主要功能是教授给我们的,我们必须围绕它构建功能。

我不知道为什么这似乎仍然会导致内存泄漏,如果我说实话,我真的不知道它们还会发生在哪里。据我所知,我需要将所有的记忆保留到几乎最后一分钟。

请帮忙,我在这里迷路了。

谢谢!

【问题讨论】:

    标签: c pointers memory-management memory-leaks


    【解决方案1】:

    有两个问题:

    1. 需要将所有malloc (sizeof(Node*))更改为malloc (sizeof(Node))
    2. 需要在kill函数中将free(header)改为free(current)

    修改后的代码如下

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    typedef struct Node {
        int data;
        struct Node *next;
        struct Node *previous;
    } Node;
    
    void print_dll(Node *head)
    {
        Node *curr = head;
        while(curr != NULL) {
            printf("%d\t", curr->data);
            curr = curr->next;
        }
        puts(" ");
    }
    
    Node *create_dll_from_array(int array [], int arrSize)
    {
        //this is a function that creates a doubly linked list
        //with the contents of the array
        Node *current = (Node *) malloc (sizeof(Node));
        current->data = array[arrSize - 1];
        current -> next = NULL;
    
        for(int i = 2; i <= arrSize; i++) {
            //create a new node
            Node *temp = (Node *)malloc(sizeof(Node));
            //I would like the dll to be in the same order as the array, I guess it isn't strictly necessary
            temp ->data = array[arrSize - i];
            temp -> next = current;
            current-> previous = temp;
            //now make temp the current
            current = temp;
        }
        current-> previous = NULL;
        return current;
    }
    
    void insert_after(Node *head, int valueToInsertAfter, int valueToInsert )
    {
        if(head != NULL) {
            Node *current = head;
    
            while(current-> data != valueToInsertAfter) {
                //this while loop brings 'current' to the end of the list if
                //the searched value is not there
                if(current-> next != NULL) {
                    current = current->next;
                } else {
                    break;
                }
            }
            //after exiting this loop, the current pointer is pointing
            //either to the last element of the dll or to the element
            //we need to insert after
    
            Node *new = (Node *) malloc (sizeof(Node));
            new->data = valueToInsert;
            new->next = current->next;
            new->previous = current;
            if(current->next != NULL) {
                (current->next)->previous = new;
            }
            current->next = new;
        }
    }
    
    void delete_element(Node *head, int valueToBeDeleted)
    {
        //work in progress
    }
    void kill(Node *head)
    {
    //this is my attempt at freeing all the nodes in the doubly linked list
        Node *current;
        while(head != NULL) {
            current = head;
            head = head->next;
            free(current);
        }
    }
    int main()
    {
        int array [5] = {11, 2, 7, 22, 4};
        Node *head;
    
        /*Question 1*/
        //creates a doubly linked list from the array below
        head = create_dll_from_array(array, 5); ///size of the array is 5
    
        /* Question 2 */
        // print_dll(head);
    
        /*Question 3*/
        // to insert 13 after the first appearance of 7
        insert_after(head, 7, 13);
        print_dll(head);
        //to insert 29 after first appearance of 21
        insert_after(head, 21, 29);
        print_dll(head);
    
        /*Question 6*/
        //create a function to free the whole list
    
        kill(head);
    
    
        return 0;
    
    }
    

    【讨论】:

      【解决方案2】:
      1. sizeof(Node * ) 更改为 sizeof(Node),因为 malloc 会为您保留指针指向的内存,并且它需要正确数量的所需内存(不是指针,而是对象本身)。
      2. i &lt;= arrSize 可能是溢出,因为大小通常以存储单元的数量给出。所以你可以考虑使用i &lt; arrSize
      3. insert_after 中的第一个 while 循环可能指向数组后面的无效内存
      4. Node *new = 是丑陋的语法,因为 new 是 C++ 中的关键字。请永远不要这样做,因为这会破坏 C++ 中使用的任何代码。
      5. kill() 中不需要临时元素。您可以改为直到 head 指向 NULL。
      6. delete_element 需要与insert_after 相同的数组检查

      您可能需要调试整个事情,一个接一个地粘贴一个函数以使其正常工作。不保证正确性,因为如果没有 cmets 和所有内容,这有点难以阅读。

      【讨论】:

      • 感谢您的回答。为什么说“Node *new =...”显然是错误的?它出什么问题了?这是我的教授在使用 malloc 时向我们展示的语法...无论如何,它与 Node *temp = 的语法相同。程序按照它应该的方式编译和运行,似乎我对何时何地释放内存有误解。
      • 啊,我明白了,我想这是一个惯例,不称事物为“新”。顺其自然,我也可以改一下,我觉得没关系,因为它只是指针的一个名字。
      • arrSize 是一个描述数组长度的 int。在循环内部,我正在处理array [arrSize-i] 形式的数组元素,因此如果我想访问array[0],让i 一直到达arrSize 似乎是合适的。
      • 啊,你要倒数。还是很奇怪。您从偏移量 2 开始向后移动。我希望从 0 或明确的评论开始。
      【解决方案3】:

      查找内存泄漏的最佳方法是在运行时使用 valgrind(或类似工具)。 Valgrind 将识别您遇到的任何内存泄漏或违规。

      要在linux环境下运行valgrind,你需要做的就是:

      # valgrind --leak-check=full ./my_program
      

      在你的情况下,它给出了主要的这些错误:

      ==28583== Invalid read of size 8
      ==28583==    at 0x400871: kill (aaa.c:77)
      ==28583==    by 0x40092D: main (aaa.c:103)
      ==28583==  Address 0x5204188 is 0 bytes after a block of size 8 alloc'd
      ==28583==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
      ==28583==    by 0x40073A: create_dll_from_array (aaa.c:29)
      ==28583==    by 0x4008D9: main (aaa.c:87)
      

      此错误表示分配大小太小。正如另一个答案中提到的,这是因为您为指针而不是结构分配了足够的内存。

      【讨论】:

        猜你喜欢
        • 2016-09-02
        • 2014-04-23
        • 2020-05-11
        • 1970-01-01
        • 1970-01-01
        • 2020-02-03
        • 2017-09-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多