usos listas lista estatica enlazadas enlazada doblemente dinamica cola codigo circulares algoritmo c segmentation-fault malloc valgrind circular-list

estatica - listas circulares usos



liberar memoria en la Lista Circular Doblemente Vinculada (2)

valgrind me dice que tengo XX bytes en XX bloques que definitivamente se perdieron record blah blah

y la fuente está en malloc, sin embargo, creo que es porque no estoy liberando memoria suficiente para malloc. De todos modos, he proporcionado el código que creo que está causando el error de montón.

Soy consciente de que no estoy liberando la memoria en list_remove, que estoy bastante seguro de que es la única fuente del problema. Probablemente requiera un cambio de temperatura, pero no sé si ese es el único problema.

list_t *list_remove(list_t *list, list_t *node) { list_t *oldnode = node; node->prev->next = node->next; node->next->prev = node->prev; if (list != oldnode) { free(oldnode); return list; } else { list_t *value = list->next == list ? NULL : list->next; free(oldnode); return value; } } void list_free(list_t *list) { if (list) { while (list_remove(list, list_last(list)) != NULL) {} } }

list last simplemente da el último nodo de la lista.

EDITAR: Lamento no haber proporcionado suficiente información, Kerrek SB, alk. Aquí está el resto del código, como se puede ver, el malloc se produce en newnode, donde puedo comenzar a crear nuevas listas. la estructura es bastante simple, con un valor y un anterior, a continuación:

#include <stdlib.h> #include <string.h> #include <stdio.h> #include "ll.h" struct list { char *value; struct list *next; struct list *prev; }; const char *list_node_value(list_t *node) { return node->value; } list_t *list_first(list_t *list) { return list; } list_t *list_last(list_t *list) { return list->prev; } list_t *list_next(list_t *node) { return node->next; } list_t *list_previous(list_t *node) { return node->prev; } static void failed_allocation(void) { fprintf(stderr, "Out of memory./n"); abort(); } static list_t *new_node(const char *value) { list_t *node = malloc(sizeof(list_t)); if (!node) failed_allocation(); node->value = malloc(strlen(value)+1); if (!node->value) failed_allocation(); strcpy(node->value, value); return node; } list_t *list_insert_before(list_t *list, list_t *node, const char *value) { list_t *insert_node = new_node(value); insert_node->prev = node->prev; insert_node->next = node; insert_node->next->prev = insert_node; insert_node->prev->next = insert_node; if (list == node) { return insert_node; } else { return list; } } list_t *list_append(list_t *list, const char *value) { if (list) { (void) list_insert_before(list, list, value); return list; } else { list_t *node = new_node(value); node->prev = node->next = node; return node; } } list_t *list_prepend(list_t *list, const char *value) { if (list) { return list_insert_before(list, list, value); } else { list_t *node = new_node(value); node->prev = node->next = node; return node; } } list_t *list_remove(list_t *list, list_t *node) { list_t *oldnode = node; node->prev->next = node->next; node->next->prev = node->prev; if (list != oldnode) { free(oldnode); return list; } else { list_t *value = list->next == list ? NULL : list->next; free(oldnode); return value; } } void list_free(list_t *list) { if (list) { while (list_remove(list, list_last(list)) != NULL) {} } } void list_foreach(list_t *list, void (*function)(const char*)) { if (list) { list_t *cur = list_first(list); do { function(cur->value); cur = cur->next; } while (cur != list_first(list)); } }

¡Por favor ayuda! Todavía me da un error de pérdida de memoria en el montón ...


El código se ve bien

¿Cómo se define list_t ? ¿ list_t tiene algún miembro haciendo referencia a la memoria asignada dinámicamente? Si es así, también debes liberar la memoria a la que hacen referencia esos.


Si está preocupado con list_free () le sugiero que endurezca la cadena de eliminación en el origen. Lo siguiente asume, cuando todo ha terminado, que desea que * list sea NULL (porque la lista completa acaba de ser eliminada).

void list_free(list_t **list) { if (list && *list) { list_t* next = (*list)->next; while (next && (next != *list)) { list_t *tmp = next; next = next->next; free(tmp); } free(*list); *list = NULL; } }

O algo así. Invocado al pasar la dirección de su puntero de lista externa:

list_t *list = NULL; .. initialize and use your list... // free the list list_free(&list);

EDITAR Después de que el OP publicó más código, un par de cosas son evidentes.

  1. list_newnode() NO establece los valores de prev y next para que contengan basura.
  2. Todas las otras funciones aquí asumen (1) inicializaron siguiente y prev correctamente. Francamente, estoy sorprendido de que esto no tenga errores en el inicio del segundo complemento.

La inserción de la lista circular debe suponer que el nuevo nodo que se está insertando puede ser la lista inicial. Parece que estás haciendo este esfuerzo mucho más difícil de lo necesario. Recuerde, una lista circular puede tener cualquier nodo como encabezado de lista, y esto se demuestra no mejor que cuando se borra la lista actual ''head''. Debe haber un mecanismo en el lugar para restablecer a la persona que llama la nueva lista ''cabeza'' cuando esto sucede. Este mismo mecanismo debe permitir que el encabezado de la lista se establezca en NULL cuando se elimine el último nodo.

Parece que su código hace intentos manifiestos de hacerlo sin utilizar punteros a los punteros, y sin embargo, hacen que la tarea de una lista circular sea mucho más fácil. Otras cosas que debes tener en cuenta en tu código:

  • La mayoría de sus funciones parecen intentar sugerirle a la persona que llama cuál debe ser el encabezado de la lista por valor de retorno. Por el contrario, deberían aplicarlo por in / out param.
  • Cualquier función que inserte un nuevo nodo en relación con otra debería devolver el nuevo nodo.
  • Las list_prepend() y list_append() deben considerar las funciones básicas de inserción relativas a un list-head. Las otras apis ( list_insert_before() , list_insert_after() , etc.) deberían ser enteramente relativas al nodo existente válido al que estás insertando antes o después, y como dije antes, devuelve siempre un puntero al nuevo nodo insertado . Verá que ambas funciones del insertador no basadas en la raíz ya no pasan el encabezado de la lista.
  • La mayoría de sus funciones de utilidad fueron correctas, salvo por no verificar punteros no válidos antes de realizar desreferencias. Todavía hay algunos que no, pero al menos es manejable ahora.

La siguiente es una base de código construida en torno a la mayoría de sus funciones. Las rutinas reales de colocación de nodos se terminaron y las comenté lo mejor que pude. La plantilla de prueba principal es bastante simple. Si hay errores importantes aquí, estoy seguro de que SO-watchtower los señalará rápidamente, pero el objetivo del código no es solo arreglar el suyo; es una cosa de aprendizaje:

#include <stdio.h> #include <stdlib.h> #include <memory.h> #include <string.h> #include <assert.h> // node structure typedef struct list_t { char *value; struct list_t *next; struct list_t *prev; } list_t; static void failed_allocation(void) { fprintf(stderr, "Out of memory./n"); abort(); } // initialize a linked list header pointer. Just sets it to NULL. void list_init(list_t** listpp) { if (listpp) *listpp = NULL; } // return the value-field of a valid list node. // otherwise return NULL if node is NULL. const char *list_node_value(list_t *node) { return (node ? node->value : NULL); } // return the next pointer (which may be a self-reference) // of a valid list_t pointer. list_t *list_next(list_t *node) { return (node ? node->next : NULL); } // return the previous pointer (which may be a self-reference) // of a valid list_t pointer. list_t *list_previous(list_t *node) { return (node ? node->prev : NULL); } // return the same pointer we were passed. list_t *list_first(list_t *headp) { return headp; } // return the previous pointer (which may be a self-reference) // of the given list-head pointer. list_t *list_last(list_t *headp) { return list_previous(headp); } // insert a new item at the end of the list, which means it // becomes the item previous to the head pointer. this handles // the case of an initially empty list, which creates the first // node that is self-referencing. list_t *list_append(list_t **headpp, const char* value) { if (!headpp) // error. must pass the address of a list_t ptr. return NULL; // allocate a new node. list_t* p = malloc(sizeof(*p)); if (p == NULL) failed_allocation(); // setup duplicate value p->value = (value) ? strdup(value) : NULL; // insert the node into the list. note that this // works even when the head pointer is an initial // self-referencing node. if (*headpp) { (*headpp)->prev->next = p; p->prev = (*headpp)->prev; p->next = (*headpp); (*headpp)->prev = p; } else { // no prior list. we''re it. self-reference *headpp = p; p->next = p->prev = p; } return p; } // insert a new value into the list, returns a pointer to the // node allocated to hold the value. this will ALWAYS update // the given head pointer, since the new node is being prepended // to the list and by-definition becomes the new head. list_t *list_prepend(list_t **headpp, const char* value) { list_append(headpp, value); if (!(headpp && *headpp)) return NULL; *headpp = (*headpp)->prev; return *headpp; } // insert a new node previous to the given valid node pointer. // returns a pointer to the inserted node, or NULL on error. list_t *list_insert_before(list_t* node, const char* value) { // node *must* be a valid list_t pointer. if (!node) return NULL; list_prepend(&node, value); return node; } // insert a new node after the given valid node pointer. // returns a pointer to the inserted node, or NULL on error. list_t *list_insert_after(list_t* node, const char* value) { // node *must* be a valid list_t pointer. if (!node) return NULL; node = node->next; list_prepend(&node, value); return node; } // delete a node referenced by the node pointer parameter. // this *can* be the root pointer, which means the root // must be set to the next item in the list before return. int list_remove(list_t** headpp, list_t* node) { // no list, empty list, or no node all return immediately. if (!(headpp && *headpp && node)) return 1; // validate the node is in *this* list. it may seem odd, but // we cannot just free it if the node may be in a *different* // list, as it could be the other list''s head-ptr. if (*headpp != node) { list_t *p = (*headpp)->next; while (p != node && p != *headpp) p = p->next; if (p == *headpp) return 1; } // isolate the node pointer by connecting surrounding links. node->next->prev = node->prev; node->prev->next = node->next; // move the head pointer if it is the same node if (*headpp == node) *headpp = (node != node->next) ? node->next : NULL; // finally we can delete the node. free(node->value); free(node); return 0; } // release the entire list. the list pointer will be reset to // NULL when this is finished. void list_free(list_t **headpp) { if (!(headpp && *headpp)) return; while (*headpp) list_remove(headpp, *headpp); } // enumerate the list starting at the given node. void list_foreach(list_t *listp, void (*function)(const char*)) { if (listp) { list_t *cur = listp; do { function(cur->value); cur = cur->next; } while (cur != listp); } printf("/n"); } // printer callback void print_str(const char* value) { printf("%s/n", value); } // main entrypoint int main(int argc, char *argv[]) { list_t *listp; list_init(&listp); // insert some new entries list_t* hello = list_append(&listp, "Hello, Bedrock!!"); assert(NULL != hello); assert(listp == hello); // insert Fred prior to hello. does not change the list head. list_t* fred = list_insert_before(hello, "Fred Flintstone"); assert(NULL != fred); assert(listp == hello); // Hello, Bedrock!! // Fred Flintstone list_foreach(listp, print_str); // insert Wilma priot to Fred. does not change the list head. list_t* wilma = list_insert_before(fred, "Wilma Flintstone"); assert(NULL != wilma); assert(list_next(wilma) == fred); assert(list_previous(wilma) == hello); // Hello, Bedrock!! // Wilma Flintstone // Fred Flintstone list_foreach(listp, print_str); list_t* barney = list_prepend(&listp, "Barney Rubble"); list_t* dino = list_insert_after(wilma, "Dino"); assert(barney != NULL); assert(dino != NULL); assert(listp == barney); assert(list_previous(barney) == fred); assert(list_next(barney) == hello); // Barney Rubble // Hello, Bedrock!! // Wilma Flintstone // Dino // Fred Flintstone list_foreach(listp, print_str); // remove everyone, one at a time. list_remove(&listp, fred); // will not relocate the list head. // Barney Rubble // Hello, Bedrock!! // Wilma Flintstone // Dino list_foreach(listp, print_str); list_remove(&listp, hello); // will not relocate the list head. // Barney Rubble // Wilma Flintstone // Dino list_foreach(listp, print_str); list_remove(&listp, barney); // will relocate the list head. // Wilma Flintstone // Dino list_foreach(listp, print_str); assert(listp == wilma); assert(list_next(wilma) == dino); assert(list_previous(listp) == dino); list_remove(&listp, wilma); // will relocate the list head. // Dino list_foreach(listp, print_str); list_remove(&listp, dino); // will relocate the list head; // generate a raft entries (a million of them)/ char number[32]; int i=0; for (;i<1000000; i++) { sprintf(number, "%d", i); list_append(&listp, number); } // now test freeing the entire list. list_free(&listp); return 0; }

La aspersión si afirma y los depósitos son para ayudar a validar la solidez del algoritmo. El resultado resultante para esto, que debe coincidir con los comentarios en el código, es:

Hello, Bedrock!! Fred Flintstone Hello, Bedrock!! Wilma Flintstone Fred Flintstone Barney Rubble Hello, Bedrock!! Wilma Flintstone Dino Fred Flintstone Barney Rubble Hello, Bedrock!! Wilma Flintstone Dino Barney Rubble Wilma Flintstone Dino Wilma Flintstone Dino Dino

Pensamientos finales: He corrido esto a través de valgrind y no encontré ninguna fuga. Estoy seguro de que no se adaptará a tus necesidades. ** La mayor parte lo hará (la mitad ya está allí ).