Утечки памяти в двусвязном списке - PullRequest
0 голосов
/ 06 марта 2019

Я довольно новичок в программировании на 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;

 }

Основная функция здесь дается нам профом, мы должны построить функцию вокруг него.

Я не знаю, почему это все еще ведет к утечкам памяти, и если я, честно говоря, я не знаю, где еще они могут произойти. Насколько я знаю, мне нужно хранить всю память почти до последней минуты.

Пожалуйста, помогите, я здесь довольно потерян.

Спасибо!

Ответы [ 3 ]

1 голос
/ 06 марта 2019

Есть две проблемы:

  1. Нужно изменить все malloc (sizeof(Node*)) на malloc (sizeof(Node))
  2. Необходимо изменить 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;

}
1 голос
/ 06 марта 2019

Лучший способ обнаружить утечки памяти - использовать valgrind (или аналогичный инструмент) во время выполнения. Valgrind определит любую утечку памяти или нарушение, через которое вы прошли.

для запуска valgrind в среде Linux, все, что вам нужно сделать, это:

# 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)

эта ошибка означает, что размер выделения был слишком мал. как уже упоминалось в других ответах, это потому, что вы выделяете достаточно памяти для указателя, а не для структуры.

1 голос
/ 06 марта 2019
  1. Измените sizeof(Node * ) на sizeof(Node) из-за того, что malloc зарезервирует вам память, на которую указывает указатель, и ему требуется правильный объем необходимой памяти (которая является не указателем, а самим объектом).
  2. i <= arrSize может быть переполнением, поскольку размер обычно задается как количество ячеек памяти. Так что вы можете рассмотреть возможность использования i < arrSize
  3. Первый цикл while в insert_after может указывать на недопустимую память после массива
  4. Node *new = - уродливый синтаксис, поскольку new - это ключевое слово в C ++. Пожалуйста, никогда не делайте этого, так как это сломает любой код, который используется в C ++.
  5. Вам не нужен временный элемент в kill(). Вместо этого вы можете идти, пока голова не укажет на NULL.
  6. delete_element требует тех же проверок массива, что и insert_after

Возможно, вам нужно отладить все, вставив одну функцию за другой, чтобы она работала правильно. Нет гарантии правильности, так как это было трудно читать без комментариев и всего.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...