c链表 – 是否可以创建有效负载独立迭代器function?

所有,在我的应用程序中,我有许多正在创建的链接列表。 例如,一个(结构记录)保存具有数百个节点(文本行)的脚本文本,第二个类型链接列表(struct srch_results)保存搜索结果,使用strstr()搜索第一个列表。 应用程序中可以有多个列表。 问题是我发现自己为每个列表类型重新创建每个正向/反向迭代器,这基本上是复制代码并更改列表的结构类型。 例如,遍历struct记录的一组函数和遍历struct srch_results的一组函数是:

// 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); } } 

有没有办法创建一个可以在任何列表上运行的通用void迭代器类型? 一些generics迭代器和genericstypedef允许传递回调函数和列表地址,以便迭代器遍历给定列表调用每个节点上提供的函数?

所有结构都具有结构地址和地址 – > next,address-> prev。 我已经尝试基于一些利用回调的其他void迭代器函数来定义void *函数,但是我没有迭代我传递的列表中的地址,而是从堆栈中获取地址。 (即list-> prev是堆栈上前一个列表的地址,而不是列表中的上一个节点)。 好消息是,通缉列表地址实际上是在回调中结束,而不是以我想要的可用方式。

 // 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); 

这可能是不可能的,但目的是将现有的struct地址作为void传递,然后使用typedef struct iterator对该地址进行操作,其中包含 – > next和 – > prev成员,等同于传递的每个列表中包含的成员。 上面的例子编译,但是当运行时,它会在回调到达堆栈上的最后一个列表时发生段错误。 (即,创建一个struct记录和一个struct srch_results,它将迭代两次,在segfaulting之前为每个列表提供地址。这样的事情是可能的,还是缺少示例我能够找到答案本身?

你有几个选择。

1)使用TAILQ宏看到这里 。

2)创建包含指向数据的通用指针的节点的链接列表:

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

然后,您可以迭代Node列表,并将data指针转换为您认为该列表包含的任何类型。 在这种情况下,必须独立于数据分配Node 。 此外,如果您从其他指针获取数据,除非数据指向Node否则您将无法找到关联的Node

3)创建一个没有数据指针的节点,并将其嵌入到数据结构中。

 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; 

然后,您可以通过向后移动指针,将指针转换为指向封闭数据的指针:

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

请注意,如果将Node放在数据的开头,则无需调整指针的值,因此您可以简单地从Node*DataA*DataB*

在这种情况下,每个数据都带有自己的Node ,并且您不需要Node和数据指向彼此。