【问题标题】:C Generic Double Linked List each node Holding Multiple ItemsC通用双链表每个节点持有多个项目
【发布时间】:2013-04-02 00:01:35
【问题描述】:

我正在尝试设计一个通用链表,其中每个节点可以存储多个项目,例如颜色、形状和大小。

这就是我的 .h 文件的样子

typedef struct linked{
    char type;
    void * item;
    struct linked * next;
    struct linked * prev;
}LinkedList;

LinkedList * NewLinkedList();
LinkedList * next(Iterator **I);
int hasNext(Iterator *I);
void insertion(LinkedList **a, void *b);
void printStringList(LinkedList *L);
void printIntList(LinkedList *L);
void printDoubleList(LinkedList *L);
void delete(LinkedList *L, void *n);

我应该通过添加 void * item2 和 void * item3 ...来修改结构吗?或者有没有更好的方法。

我的第二个问题是:每次使用 void 指针时都必须对它们进行类型转换吗?这是我的printIntList代码

void printList(LinkedList *L)
{
    LinkedList *tmp = NULL;
    tmp = L;
    while(L)
    {
        printf("%d\n",*(int*)L->item);
        L=L->next;
    }
}

我有printIntListprintDoubleListprintStringList。有什么办法可以将它们结合起来吗?

谢谢!

【问题讨论】:

    标签: c generics linked-list


    【解决方案1】:

    您需要考虑每个元素定义的项目是否真的是动态的。颜色、大小、形状是否是您需要存储的极限?

    如果这就是你真正需要的,为什么不保持简单并尽量减少间接/内存分配:

    enum ShapeFlags { sizeDefined = 0x01, colorDefined = 0x02, shapeDefined = 0x04 };
    
    typedef struct shapeList {
        ShapeFlags shapeFlags;
        Size  size;
        Color color;
        Shape shape;
        struct Linked * next;
        struct Linked * prev;
    } ShapeList;
    

    另一方面,如果您确实拥有一组非常动态的属性,则列表列表的替代方法可能是拥有一个带有哨兵的列表。

    enum FieldFlag { terminator = 0x01, size = 0x02, color = 0x04, shape = 0x08 };
    
    typedef struct list
    {
      FieldFlag type;
      void* value;
      struct list* next;
      struct list* previous;
    } List;
    

    然后您可以正常迭代并使用terminator 标志来了解对象的结尾。

    【讨论】:

      【解决方案2】:

      是的,您可能应该使用三个指针,或者某些形状可以没有颜色或大小?如果没有,您应该考虑创建三个指针,每个指针都有它们所指向的类型,这样您就不必强制转换和删除类型。

      结构可能如下所示:

      typedef struct linked{
          Size * size;
          Color* color;
          Shape* shape;
          struct linked * next;
          struct linked * prev;
      }LinkedList;
      

      如果您确实想在一个节点中存储更多相同类型的项目(例如两种颜色...),您应该考虑使用union

      要回答第二个问题...是的,您应该在每次使用它们时投射void 指针(至少在您的情况下=)。您应该再次考虑 union 以获得更漂亮的代码...

      【讨论】:

        【解决方案3】:

        我应该通过添加 void * item2 和 void * item3 ...来修改结构吗?或者有没有更好的方法。

        这取决于您的列表的通用程度。如果它用于保存一些特定的值,你可以创建它们,或者为它们创建一个结构,如果它可能保存不同类型的值,你可能想要创建一个联合,如果它可能在一个节点中保存许多值,您可能最好创建一些值数组。

        是否每次使用 void 指针时都必须对它们进行类型转换?

        当您分配给/从他们(int * a = a_void_pointer / a_void_pointer = an_int_pointer)时 - 否

        当您取消引用此类指针 (*a_void_pointer) 时 - 是的。

        【讨论】:

          【解决方案4】:

          您可以使用可区分的联合类型创建一个通用列表:

          typedef struct listitem
          {
            char type;
            union
            {
              integer i;
              double d;
              char *s;
              // etc.
            } u;
          } Listitem;
          

          在您的 LinkedList 中使用此列表项。

          【讨论】:

            【解决方案5】:

            你可以这样做:

            typedef struct links {
                struct links * next;
                struct links * prev;
            } links;
            
            typedef struct outer_node {
              links l; // links to outer_node.l
              struct inner_node * children;
            } outer_node;
            
            typedef struct inner_node {
              links l; // links to inner_node.l
              char type;
              void * item;
            } inner_node;
            

            基本上,你有一个链表,它的每个节点都是它自己的链表。

            两个列表都使用相同类型的链接进行组织,因此您无需复制链接/取消链接代码。

            您将需要使用offsetof() 宏从指向links 的指针获取其容器inner_nodeouter_node

            这是一个完整的演示:

            #include <stdio.h>
            #include <stdlib.h>
            #include <stddef.h>
            
            typedef struct links {
                struct links * next;
                struct links * prev;
            } links;
            
            typedef struct outer_node {
              links l; // links to outer_node.l
              struct inner_node * children;
            } outer_node;
            
            typedef struct inner_node {
              links l; // links to inner_node.l
              char type;
              void * item;
            } inner_node;
            
            void linkNodes(links* l1, links* l2)
            {
              links* tmp = l1->prev;
              l1->next->prev = l2->prev;
              l1->next = l2;
            
              l2->prev->next = tmp;
              l2->prev = l1;
            }
            
            inner_node* createNode(char type, void* item, inner_node* sibling)
            {
              inner_node* n = malloc(sizeof(*n));
              n->type = type;
              n->item = item;
              n->l.prev = &n->l;
              n->l.next = &n->l;
              if (sibling != NULL)
                linkNodes(&n->l, &sibling->l);
              return n;
            }
            
            outer_node* createOuterNode(inner_node* child, outer_node* sibling)
            {
              outer_node* n = malloc(sizeof(*n));
              n->l.prev = &n->l;
              n->l.next = &n->l;
              n->children = child;
              if (sibling != NULL)
                linkNodes(&n->l, &sibling->l);
              return n;  
            }
            
            void PrintList(links* l, int level)
            {
              links* l2 = l;
              do
              {
                if (level == 0)
                {
                  outer_node* n = (void*)((char*)l2 + offsetof(outer_node, l));
                  printf("{\n");
                  PrintList(&n->children->l, level + 1);
                  printf("}\n");
                }
                else
                {
                  inner_node* n = (void*)((char*)l2 + offsetof(inner_node, l));
                  printf("  type: %d, item: %s\n", n->type, (char*)n->item);
                }
                l2 = l2->next;
              } while (l2 != l);
            }
            
            int main(void)
            {
              outer_node* root = 
                createOuterNode(
                  createNode(0, "something",
                    NULL
                  ),
                  createOuterNode(
                    createNode(1, "foo",
                      createNode(2, "bar",
                        NULL
                      )
                    ),
                    createOuterNode(
                      createNode(3, "red",
                        createNode(3, "green",
                          createNode(3, "blue",
                            NULL
                          )
                        )
                      ),
                      createOuterNode(
                        createNode(4, "cat",
                          createNode(5, "dog",
                            createNode(6, "raven",
                              createNode(7, "shark",
                                NULL
                              )
                            )
                          )
                        ),
                        NULL
                      )
                    )
                  )
                );
            
              PrintList(&root->l, 0);
            
              return 0;
            }
            

            输出(ideone):

            {
              type: 0, item: something
            }
            {
              type: 1, item: foo
              type: 2, item: bar
            }
            {
              type: 3, item: red
              type: 3, item: green
              type: 3, item: blue
            }
            {
              type: 4, item: cat
              type: 5, item: dog
              type: 6, item: raven
              type: 7, item: shark
            }
            

            【讨论】:

            • 他将如何迭代?
            • @Kupto 似乎是什么问题?我提到了offsetof()
            • 哇,不,我真的无法解决这个问题,我很累(我不想打扰你),但它应该如何工作?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多