Удаление всех элементов из очереди

#c #data-structures #queue

#c #структуры данных #очередь

Вопрос:

Допустим, у меня следующая структура

 typedef struct node {
    unsigned long data;
    struct node* next;
} node;

typedef struct queue {
    node* head;
    node* tail;
    int size;
    int capacity;
} queue;

queue* buildar()
{
    queue* arr = malloc(num * sizeof(queue));
    int i;
    for (i = 0; i < num; i  )
    {
        queue* r = malloc(sizeof(queue));
        r->head = NULL;
        r->tail = NULL;
        r->size = 0;
        r->capacity = assoc;
        arr[i] = *r;
    }
    return arr;
}
 

Как я могу освободить все элементы в очереди?
Я уже пытался это сделать

     for (size_t i = 0; i < numberofsets; i  )
    {
        node* temp = arr[i].head;
        arr[i].head = arr[i].head->next;
        free(temp);
    }
    free(arr);
 

но, похоже, это не работает. Любая помощь приветствуется.

Комментарии:

1. У вас есть массив очередей, а не одна очередь. Это действительно то, что вы намеревались?

2. Да, это именно то, что я хочу @kaylum

3. Затем вам нужен внутренний цикл, который обходит next указатели, освобождая каждый узел. И затем, наконец, вам нужно free(queue[i])

4. Ваша структура массива очередей выглядит неправильно. Или, по крайней мере, не очень. У вас должен быть массив queue * вместо массива queue . На данный момент у вас есть утечки памяти. В buildar значениях r указателя теряются и, следовательно, никогда не могут быть освобождены.

5. Пожалуйста, подробно опишите, как «кажется, это не работает». В вашем цикле распределения, где вы выделяете массив корней очереди, вам не нужен внутренний malloc() ; удалите это и замените все r-> на arr[i] . Для вашего free() цикла вы освобождаете только самый первый элемент в каждой очереди; вам нужно следовать next , чтобы освободить все в каждой очереди.

Ответ №1:

Вы ищете массив массивов (я думаю / надеюсь). По сути, вы хотите создать n очереди, которые все индексируются (это слово?) Из одного «главного» массива.

Итак, вы смотрите на это так:

 idx
of
big
arry
_________________________________  
|000|   ----->  [ one big queue ]  
|001|   ----->  [ one big queue ]  
|002|   ----->  [ one big queue ]  
|003|   ----->  [ one big queue ]  
|004|   ----->  [ one big queue ]  
|...|   ----->  .................   
|nth|   ----->  [ one big queue ]  
_________________________________  
 
 #include <stdlib.h>


typedef struct node
{
    unsigned long data;
    struct node*  next;
}
node;

typedef struct queue
{
    int   size;
    int   capacity;
    node* head;
    node* tail;
    
}
queue;


queue** build_queue(int num  ,
                    int assoc
                   )
{
    //here you create an array of arrays
    //result is an array of pointers, each pointer going to a separate array 
    queue** result = malloc(num * sizeof(queue*));
    
    int i;
    for (i = 0; i < num; i  )
    {
        queue* q    = malloc(sizeof(queue));  //memory for actual queue
        result[i]   = q;

        q->head     = NULL;
        q->tail     = NULL;
        q->size     = 0;
        q->capacity = assoc;
    }

    return resu<
}



void free_queues(int     num_queues,
                 queue** qs
                )
{
    int i;
    for (i = 0; i < num_queues; i  )
    {
       /*
        * NOTE: you must use a while loop here to FIRST free the linked list qs[i]
        * node* head = qs[i]->head;
        * while(head != qs[i]->tail)
        * {
        *     node* temp = head->next;
              free(head);
              head = temp;
        * }
        * free(qs[i]->tail);
        */
       free(qs[i]);  //frees the actual queue struct   
    }
    free(qs);        //frees the array of pointers to queues
}



int main(void)
{
   int num   = 5;
   int assoc = 4;
 
   queue** qs = build_queue(num, assoc);
   free_queues(num, qs);

   return 1;
}
 

Наконец, используйте valgrind для проверки наличия утечек. Для приведенного выше решения:

 $ gcc please_work.c
$ valgrind ./a.out 
==5974== Memcheck, a memory error detector
==5974== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5974== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==5974== Command: ./a.out
==5974== 
==5974== 
==5974== HEAP SUMMARY:
==5974==     in use at exit: 0 bytes in 0 blocks
==5974==   total heap usage: 6 allocs, 6 frees, 160 bytes allocated
==5974== 
==5974== All heap blocks were freed -- no leaks are possible
==5974== 
==5974== For lists of detected and suppressed errors, rerun with: -s
==5974== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

 

Ответ №2:

Трудно догадаться, что означает «похоже, не работает», но есть несколько проблем с кодом.

В buildar() вы используете две неопределенные переменные. Они могут быть у вас как глобальные переменные, но вы, вероятно, хотите, чтобы они были аргументами функции. Вы также допускаете утечку памяти, потому что вы malloc() создаете новую ссылку, инициализируете ее, копируете значения там в массив, а затем выбрасываете выделенную память, которая теперь никогда не будет освобождена.

 // added num amp; assoc here--assuming it should be a parameter
queue* buildar(int num, int assoc) 
{
    queue* arr = malloc(num * sizeof(queue));
    int i;
    for (i = 0; i < num; i  )
    {
        // This will leak memory. You allocate a root
        // that you initialise, then you copy the data
        // and then you throw away the allocated memory
        // when you update r, or it goes out of scope
        queue* r = malloc(sizeof(queue));
        r->head = NULL;
        r->tail = NULL;
        r->size = 0;
        r->capacity = assoc;
        arr[i] = *r;
    }
    return arr;
}
 

Поскольку у вас уже есть корневые ссылки в массиве, вы можете просто обновить их и избежать malloc() :

 queue* buildar(int num, int assoc) 
{
    queue* arr = malloc(num * sizeof *arr);
    for (int i = 0; i < num; i  )
    {
        arr[i].head = NULL;
        arr[i].tail = NULL;
        arr[i].size = 0;
        arr[i].capacity = assoc;
    }
    return arr;
}
 

В коде освобождения, если корневые узлы недавно инициализированы, их head указатель NULL . Это будет проблемой, когда вы разыменуете их, чтобы получить head->next . Технически разыменование NULL — это неопределенное поведение, но на практике это будет ошибка сегментации, которая приведет к сбою вашей программы. Если на запись приходится более одного узла, произойдет утечка памяти. Вы освобождаете первый после корня, затем указываете заголовок корня на следующий, а затем освобождаете массив, содержащий корень.

Насколько я вижу, код будет работать по назначению, если для каждого индекса есть ровно один узел (после корня), но сбой, если есть ноль, и утечка, если их больше. Если предполагается, что число узлов должно быть переменным, это один из источников «похоже, не работает». Может ли это быть так?

 void free_queues(queue *arr, int numberofsets)
{
  for (size_t i = 0; i < numberofsets; i  )
  {
    node* temp = arr[i].head;
    // if the queue was empty, arr[i].head->next
    // dereferences a NULL pointer, which is
    // undefined behaviour
    arr[i].head = arr[i].head->next;
    // This only removes one link from the queue.
    // If there are more, you leak memory when you
    // free arr below
    free(temp);
  }
  free(arr);
}
 

Если это так, вам может понадобиться что-то вроде:

 void free_queues(queue *arr, int numberofsets)
{
  for (size_t i = 0; i < numberofsets; i  ) {
    node* temp = arr[i].head, *next;
    while (temp) {
      next = temp->next;
      free(temp);
      temp = next;
    }
  }
  free(arr);
}
 

Комментарии:

1. Интересно, действительно ли это то, чего хочет OP. Кажется, он уверен, что внутренний malloc необходим. Я думаю, что это назначение симулятора кэша, и они пытаются имитировать ассоциативность кэша L1.

2. Это может быть, но сейчас он ничего не делает, кроме утечки памяти. Тогда, может быть, это должен быть массив указателей на узлы?

3. Я думаю в том же духе. Массив указателей на очереди. Они добавляют элемент в очередь, затем увеличивают количество r->size = 1 . О боже.. Я потратил на это слишком много времени. Вы в восторге от AlphaFold?

4. @slmatrix AlohaFold — это не очень круто, но я сам не работал над сворачиванием белка, поэтому я точно не знаю, где он превосходит проблему