una resueltos listas lista insertar hacer fuente estatica enlazadas eliminar elementos ejercicios ejemplos código como codigo buscar ats agregar c linked-list iterator

resueltos - c lista enlazada: ¿es posible crear una función de iterador independiente de la carga útil?



listas enlazadas ats (1)

Todos, en mi aplicación tengo una cantidad de listas enlazadas que se están creando. Por ejemplo, uno (registro de estructura) contiene el texto de la transcripción con varios cientos de nodos (líneas de texto) y una segunda lista vinculada de tipo (struct srch_results) mantiene los resultados de la búsqueda de la primera lista con strstr (). Puede haber múltiples listas de cada uno dentro de la aplicación. El problema es que me encuentro recreando cada iterador adelante / atrás para cada tipo de lista que básicamente está duplicando el código y cambiando el tipo de estructura para la lista. Por ejemplo, un conjunto de funciones que atraviesan el registro de estructura y un conjunto que atraviesa struct srch_results son:

// Simple structure to use as the base for depo double linked list struct record { char *line; int lineno; int linetype; struct record *prev; struct record *next; }; typedef struct record rec; // Simple structure to use as the base for search results double linked list struct srch_results { char *lineptr; struct record *node; struct srch_results *prev; struct srch_results *next; }; typedef struct srch_results srchres; // general functions to operate on ''rec'' type void rec_prn_node (rec *node) { fprintf (stdout, "%s() prev: %p cur: %p next: %p/n", __func__, node->prev, node, node->next); } void rec_prn_payload (rec *node) { fprintf (stdout, "%s() lineno: %d, linetype: %d line: %s/n", __func__, node->lineno, node->linetype, node->lineptr); } void rec_iterfwd (void fnc (srchres *list), srchres *list) { rec *iter = list; // second copy to iterate list if (iter == NULL) { fprintf (stdout,"%s(), The list is empty/n",__func__); } else { do { fnc (iter); iter = iter->next; } while (iter != list); } } // general functions to operate on ''srchres'' type void srch_prn_node (srchres *node) { fprintf (stdout, "%s() prev: %p cur: %p next: %p/n", __func__, node->prev, node, node->next); } void srch_prn_payload (srchres *node) { fprintf (stdout, "%s() node: %p lineptr: %s/n", __func__, node->node, node->lineptr); } void srch_iterfwd (void fnc (srchres *list), srchres *list) { srchres *iter = list; // second copy (set to last) to iterate list if (iter == NULL) { fprintf (stdout,"%s(), The list is empty/n",__func__); } else { printf ("in %s()/n", __func__); do { fnc (iter); iter = iter->next; } while (iter != list); } }

¿Hay alguna manera de crear un tipo genérico de iterador vacío que pueda operar en cualquiera de las listas? ¿Algún tipo de iterador genérico y typedef genérico que permitiría pasar una función de devolución de llamada y una dirección de lista para que el iterador recorra la lista dada llamando a la función proporcionada en cada nodo?

Todas las estructuras tienen la dirección de estructura y dirección-> siguiente, dirección-> prev. Intenté definir las funciones void * en función de algunas de las otras funciones del iterador vacío que utilizan una devolución de llamada, pero en lugar de iterar las direcciones dentro de la lista que paso, parece que obtengo la dirección de la pila. (es decir, list-> prev es la dirección de la lista anterior en la pila en lugar del nodo anterior dentro de la lista). La buena noticia es que la dirección de la lista de los buscados realmente está terminando en la devolución de llamada, pero no de la manera que yo quería.

// generic linked-list-iterator struct to hold (prev: cur: next:) pointers struct lliterator { struct lliterator *prev; struct lliterator *next; }; typedef struct lliterator lliter; void * srch_prn_node (lliter *node) { fprintf (stdout, "%s() prev: %p cur: %p next: %p/n", __func__, node->prev, node, node->next); return NULL; } void iterator (void *fnc (void *list), void *list) { lliter *iter = list; // second copy (set to last) to iterate list if (iter == NULL) { fprintf (stdout,"%s(), The list is empty/n",__func__); } else { do { fnc (iter); iter = iter->next; } while (iter != list); } } // called in the code as iterator ((void *)srch_prn_node2, (void *)sresults);

Esto puede no ser posible, pero la intención era pasar la dirección de estructura existente como nula y luego operar en esa dirección con typedef struct iterator que contiene tanto -> next y -> prev miembros equivalentes a los contenidos en cada una de las listas pasadas. El ejemplo anterior compila, pero cuando se ejecuta, segmenta cuando la devolución de llamada alcanza lo que parece ser la última lista en la pila. (es decir, con un registro struct y un struct srch_results creados, se repetirá dos veces proporcionando la dirección para cada lista antes de segfaulting. ¿Es posible algo así o es la falta de ejemplos que pude encontrar una respuesta en sí misma?


Tienes varias opciones.

1) Utilice las macros TAILQ ver aquí .

2) Cree una lista enlazada de nodos que contenga un puntero genérico a los datos:

typedef struct Node { struct Node *prev; struct Node *next; void *data; } Node;

Luego puede iterar sobre una lista de Node , y convertir el puntero de data al tipo que crea que contiene esa lista. En este caso, el Node debe asignarse independientemente de los datos. Además, si llega a los datos de otro puntero, no podrá encontrar el Node asociado a menos que los datos vuelvan al Node .

3) Cree un nodo sin puntero de datos e incrústelo en su estructura de datos.

typedef struct Node { struct Node *prev; struct Node *next; } Node; typedef struct DataA { char something; Node node; int whatever; } DataA; typedef struct DataB { char dunno; Node node; int noclue; } DataB;

A continuación, puede convertir desde un puntero a un Node a un puntero a los datos adjuntos moviendo el puntero hacia atrás:

DataA *p = (DataA*)((uintptr_t)pNodeA - offsetof(DataA, node)); DataB *p = (DataB*)((uintptr_t)pNodeB - offsetof(DataB, node));

Tenga en cuenta que si coloca el Node al comienzo de los datos, no necesita ajustar el valor del puntero, por lo que simplemente puede convertir desde el Node* a DataA* o a DataB* .

En este caso, cada dato viene con su propio Node , y no es necesario que el Node y los datos se señalen entre sí.