【问题标题】:Generic linked list?通用链表?
【发布时间】:2018-04-26 12:18:40
【问题描述】:

我想知道是否可以在 C 中为链表使用通用函数,(我不想在 C++ 中这样做,但在 C 中)示例:

struct first_struct
{
    struct first_struct *next;
    int a;
    int b;
};
struct second_struct
{
     struct second_struct *next;
     int a;
     int b;
     int c; // just one more variable than first-struct
};

我是否每次都强制为两个列表创建一个函数:

add_node(struct first_struct *mystruct)// doesn't matter the function here juste let's assume they add correctly a node
add_node1(struct second_struct *mystruct)
//and so on each time i want to make some others function always make them twice

或者有更好的方法吗?

【问题讨论】:

    标签: c linked-list polymorphism


    【解决方案1】:

    更好的方法是抽象出链接处理(使结构成为列表节点的原因),然后通过使用节点结构启动每个可列表结构来重新使用它。

    像这样:

    struct list_node {
      struct list_node *next;
    };
    
    
    struct first_struct {
      struct list_node list_node;
      int a;
      int b;
    };
    
    
    struct second_struct {
      struct list_node list_node;
      int a;
      int b;
      int c;
    };
    

    然后列出处理(指向)struct list_node 的函数。

    这通常称为“侵入式列表”,因为它要求应用程序级数据结构“知道”可以将其放入列表中。这也意味着一个结构的实例一次只能在一个列表中。

    另一种方法是创建一个仅处理指向数据的指针的列表库 (void *),它消除了限制但带来了其他限制(更多的堆分配,当数据很小时很烦人)。

    【讨论】:

    • 我还没有真正理解它是如何工作的,我怎样才能将一个简单的节点添加到列表 second_struct 中,例如通过处理 list_node 上的指针?我想我会读一些关于侵入性列表的文档
    【解决方案2】:

    就个人而言,我已经实现了一个通用链表:它通过提供一个比较两个节点的函数和一个销毁节点的可选函数(空闲字符串、关闭文件等)来工作。

    #include <stdbool.h>
    #include <stddef.h>
    
    typedef struct link {
        void        *data;
        struct link *previous;
        struct link *next;
    } link_s;
    
    typedef struct list {
        link_s *head;
        link_s *tail;
        size_t nbLink;
    
        /* function pointer */
        int    (*Data_Compare)(const void *data1, const void *data2);
        void   (*Data_Destructor)(void *data);
    } list_s;
    
    #define LIST_CONSTRUCTOR(f_compar, f_destructor) {.head = NULL, \
                                                      .tail = NULL, \
                                                      .nbLink = 0, \
                                                      .Data_Compare = f_compar, \
                                                      .Data_Destructor = f_destructor}
    
    void List_Constructor(list_s *self, int (*Data_Compare)(const void *data1, const void *data2), void (*Data_Destructor)(void *data));
    void List_Destructor(list_s *self);
    
    bool List_Add(list_s *self, void *data);
    
    void *List_RemoveByLink(list_s *self, link_s *link);
    void *List_RemoveByData(list_s *self, void *data);
    void *List_RemoveByCondition(list_s *self, bool (*Data_Condition)(const void *data));
    
    void List_DestroyByLink(list_s *self, link_s *link);
    void List_DestroyByData(list_s *self, void *data);
    
    void List_DestroyByCondition(list_s *self, bool (*Data_Condition)(const void *data));
    
    void List_Sort(list_s *self);
    void List_Merge(list_s *to, list_s *from);
    void List_Reverse(list_s *self);
    

    这样,您可以将任何您想要的内容添加到列表中。请注意具有适当的比较功能和销毁功能。

    【讨论】:

      【解决方案3】:

      您可以在结构中使用 void 指针相当容易地实现通用链表。

      这是我创建的此类列表的示例:

      list.c

      #include <stdlib.h>
      #include "list.h"
      #include <malloc.h>
      #include <stdio.h>
      #include <string.h>
      
      void listNew(list* list, unsigned int elementSize, freeMemory freeFn,        
      printList print) {
      
          list->numOfElem = 0;
          list->freeFn = freeFn;
          list->pr = print;
          list->head = NULL;
          list->sizeOfElem = elementSize;
      }
      
      node * listPushFront(list *list, void* data) {
      
          node *listNode = (node*)malloc(sizeof(node));
      
          if (listNode == NULL) {
      
              return NULL;
          }
      
          listNode->object = malloc(sizeof(list->sizeOfElem));
      
          if (listNode->object == NULL) {
      
              return NULL;
          }
      
          memcpy(listNode->object, data, list->sizeOfElem);
      
          listNode->pNext = list->head;
          list->head = listNode;
      
          list->numOfElem++;
      
          return listNode;
      }
      
      void listDestroy(list *list)
      {
          node *current;
      
          while (list->head != NULL) {
              current = list->head;
              list->head = current->pNext;
      
              if (list->freeFn) {
                  list->freeFn(current->object);
              }
      
              free(current->object);
              free(current);
          }
      }
      
      void listPrint(list *l) {
      
          node* temp = l->head;
          int i = 0;
      
          if (temp == NULL) {
      
              printf("\nEmpty list.");
              return;
          }
      
          while (temp) {
      
              printf("\nPrint element %d", i);
      
              l->pr(temp->object);
              temp = temp->pNext;
      
              i++;
          }
      }
      

      list.h

      #ifndef __LIST_H
      #define __LIST_H
      
      typedef void(*freeMemory)(void*);
      typedef void(*printList)(void*);
      
      typedef struct _node {
      
          void* object;
          struct _node* pNext;
      
      }node;
      
      typedef struct _list {
      
          unsigned int sizeOfElem;
          unsigned int numOfElem;
          node* head;
          freeMemory freeFn;
          printList pr;
      
      }list;
      
      void listNew(list* list, unsigned int elementSize, freeMemory freeFn,             
      printList print);
      node * listPushFront(list *list, void* data);
      void listDestroy(list *list);
      void listPrint(list *l);
      
      #endif
      

      ma​​in.c

      #include <stdlib.h>
      #include "list.h"
      #include <stdio.h>
      #include <string.h>
      
      typedef struct _TLV {
      
          unsigned int tag;
          unsigned int length;
          unsigned char* value;
      
      }TLV;
      
      
      void listFree(void* data) {
      
          TLV** ptr = (TLV**)data;
      
          free((*ptr)->value);
      }
      
      void Print(void* data) {
      
          TLV** ptr = (TLV**)data;
      
          printf("\nTag = %d", (*ptr)->tag);
          printf("\nLength = %d", (*ptr)->length);
          printf("\nValue = ");
      
          for (int i = 0; i < (*ptr)->length; i++) {
      
              printf("%d", (*ptr)->value[i]);
          }
      }
      
      TLV* allocateTLV(unsigned int tag, unsigned int length, unsigned char* 
      value) {
      
          TLV* elem = (TLV*)malloc(sizeof(TLV));
      
          if (elem == NULL) {
      
              return NULL;
          }
      
          elem->tag = tag;
          elem->length = length;
          elem->value = (unsigned char*)malloc(length);
      
          if (value == NULL) {
      
              return NULL;
          }
      
          memcpy(elem->value, value, length);
      
          return elem;
      }
      
      int main()
      {
          list l;
          TLV* tag;
      
          unsigned char test2[2] = { 1,2 };
          unsigned char test3[3] = { 1,2,3 };
          unsigned char test4[4] = { 1,2,3,4};
      
          listNew(&l, sizeof(TLV*), listFree, Print);
      
          tag = allocateTLV(2, sizeof(test2), test2);
          if (tag != NULL) {
      
              listPushFront(&l, &tag);
          }
      
          tag = allocateTLV(3, sizeof(test3), test3);
          if (tag != NULL) {
      
              listPushFront(&l, &tag);
          }
      
          tag = allocateTLV(4, sizeof(test4), test4);
          if (tag != NULL) {
      
              listPushFront(&l, &tag);
          }
      
          listPrint(&l);
      
          listDestroy(&l);
      
          return 0;
      }
      

      main.c 是创建指向结构 TLV 的指针列表的示例。

      【讨论】:

      • 好的,我在理解你的代码时遇到了一些困难,所以也许我误解了一些东西,但我并没有真正得到 struct _list 的实用性,因为你在一开始就分配了这个有了良好的数据,并且在您总是调用 pushFront 以将节点添加到 struct 节点之后,但是为什么不只使用 struct 节点并制作一些函数,我们提供对象长度等数据并直接处理 struct 节点没有 struct _list ?
      【解决方案4】:

      你可以使用C的两个特性来实现一个通用链表,即void指针和函数指针。

      后者(函数指针)对于构建链表并不重要,但如果你想对链表的数据做一些有用的事情,比如打印,它就很重要了。

      这是一个完整的工作示例:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      
      typedef struct node {
          void *data;
          struct node *next;
      } node;
      
      
      int lst_nodeAdd(
          node **head,
          node **tail,
          const void *data,
          size_t szData);
      
      void lst_nodePrint(
          node *head,
          void(*print)(const void *));
      
      void lst_nodeFree(node *head);
      
      /* PRINTING FUNCTIONS */
      void print_int(const void *a);
      void print_string(const void *str);
      
      
      int main(void)
      {
          const char *str[] = {
              "0x0001",
              "0x0002",
              "0x0003",
          };
      
          // head & tail
          node *head = NULL; 
          node *tail = NULL;
      
          // List of strings
          lst_nodeAdd(&head, &tail, str[0], strlen(str[0]) + 1);
          lst_nodeAdd(&head, &tail, str[1], strlen(str[1]) + 1);
          lst_nodeAdd(&head, &tail, str[2], strlen(str[2]) + 1);
      
          lst_nodePrint(head, print_string);
          lst_nodeFree(head);
          head = NULL;
          tail = NULL;
          //....................................................
      
          // List of ints
          int int_array[] = {
              0,
              1,
              2,
          };
          lst_nodeAdd(&head, &tail, &int_array[0], sizeof(int));
          lst_nodeAdd(&head, &tail, &int_array[1], sizeof(int));
          lst_nodeAdd(&head, &tail, &int_array[2], sizeof(int));
      
          lst_nodePrint(head, print_int);
          lst_nodeFree(head);
          head = NULL;
          tail = NULL;
      
      
          system("PAUSE");
          return 0;
      }
      
      
      int lst_nodeAdd(
          node **head,
          node **tail,
          const void *data,
          size_t szData)
      {
          void *tmp;
          tmp = malloc(sizeof(node));
      
          if (!tmp)
          {
              return 0;
          }
      
          ((node *)tmp)->next = NULL;
          ((node *)tmp)->data = malloc(szData);
      
          if (!((node *)tmp)->data)
          {
              free(tmp);
              return 0;
          }
      
          memcpy(((node *)tmp)->data, data, szData);
      
          if (!*head)
          {
              *head = (node *)tmp;
          }
          else
          {
              (*tail)->next = (node *)tmp;
          }
          *tail = (node *)tmp;
          return 1;
      }
      
      void lst_nodePrint(
          node *head,
          void(*print)(const void *))
      {
          while (head)
          {
              print(head->data);
              head = head->next;
          }
      }
      
      void lst_nodeFree(node *head)
      {
          node *tmp = head;
          while (head)
          {
              head = head->next;
              free(tmp->data);
              free(tmp);
              tmp = head;
          }
      }
      
      void print_int(const void *a)
      {
          printf("%d\n", *(const int *)a);
      }
      
      void print_string(const void *str)
      {
          puts((const char *)str);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-04
        • 2016-10-01
        • 1970-01-01
        相关资源
        最近更新 更多