【问题标题】:Single function to work on two types of linked lists处理两种类型的链表的单个函数
【发布时间】:2013-05-07 19:00:36
【问题描述】:

我有两个链表:-

struct Struct_A_s {
int a;
struct  Struct_A_s *next;
}Struct_A;

struct Struct_B_s {
int a;
int b;
int c;
int d;
struct  Struct_B_s *next;
}Struct_B;

我已经有几个在 Struct_A 上运行的函数。 像这样的事情:

“Add_tail(Struct_A *)”、“Remove_tail(Struct_B *)”、“Add_node(Struct_A *,int pos)、Add_head等都到位了。

对我的要求是我应该修改现有的函数,以便它们可以在 Struct_A 和 Struct_B 上运行。

在 C 中,有没有一种方法可以传递 void 指针(或类似的东西)并编写一些通用代码来处理 Struct_A 和 Struct_B。 代码大小在这里是一个大问题。目前我看到的唯一选择是从头开始为 Struct_B 重写所有代码(链表操作)。

【问题讨论】:

  • 这将是 struct 而不是 Struct。而且你在最后一个 Struct_B 之后缺少一个 ;。最好在发布到 SO 之前编译代码。
  • 已根据您的 cmets 进行更正
  • @Rüppell'sVulture 这两种结构都在上面列出?我确定我的问题不取决于结构类型。
  • 目标是让一个列表同时包含Struct_A 元素和Struct_B 元素吗?

标签: c struct linked-list


【解决方案1】:

有几种方法可以解决。而且我敢肯定,其他人比我可能有其他人建议的更熟练。

  • 选项 A:传递一个 void 指针和某种数据类型指示,然后进行转换。
  • 选项 B:将两个指针组合成一个并集并传递它。
  • 选项 C:将类型和联合组合成一个结构并传递它
  • 选项 D:使用更受尊重的替代方案来代替联合

一些示例代码...

enum DataType { TYPE_1, TYPE_2 };

struct Type1 { ... };
struct Type2 { ... };

union Data
{
    void*         addr;
    struct Type1* type1;
    struct Type2* type2;
}

struct Object
{
    DataType type;
    union Data data;
}

struct Thing
{
    DataType type;
    void* data;
}

void someFunc1( void* data, DataType type )
{
   switch( type )
   {
       case TYPE_1:
       {
           struct Type1* type1 = (struct Type1*)data;
           ...
           break;
       }
       case TYPE_2:
       {
           struct Type2* type2 = (struct Type2*)data;
           ...
           break;
       }
   }
}

void someFunc2( union Data* data, DataType type )
{
   switch( type )
   {
       case TYPE_1:
       {
           struct Type1* type1 = data->type1;
           ...
           break;
       }
       case TYPE_2:
       {
           struct Type2* type2 = data->type2;
           ...
           break;
       }
   }
}

void someFunc3( struct Object* object )
{
   switch( object->type )
   {
       case TYPE_1:
       {
           struct Type1* type1 = object->data.type1;
           ...
           break;
       }
       case TYPE_2:
       {
           struct Type2* type2 = object->data.type2;
           ...
           break;
       }
   }
}


void someFunc4( struct Thing* thing )
{
   switch( thing->type )
   {
       case TYPE_1:
       {
           struct Type1* type1 = (struct Type1*)thing->data;
           ...
           break;
       }
       case TYPE_2:
       {
           struct Type2* type2 = (struct Type2*)thing->data;
           ...
           break;
       }
   }
}

换个角度看……如果你有一个只需要a的方法,那么你可以做这样的事情……

void myFunc( int a ) { ... };

void someFunc3( struct Object* object )
{
    myFunc( object->type == TYPE_1 ? object->data.type1->a : object->data.type2->a );
}

更好的是...修改函数以接受对象并仅在底部区分...

void myFunc( struct Object* object )
{
   int* a;

   switch( object->type )
   {
       case TYPE_1:
       {
           a = &object->data.type1->a
           break;
       }
       case TYPE_2:
       {
           a = &object->data.type2->a;
           break;
       }
       default:
       {
           abort();
       }
   }

   // do work with a as though it were passed in as a pointer to int

   if( object->type == TYPE_2 )
   {
      // do additional work with the b, c, d elements, etc.
   }
}

【讨论】:

  • @k SCOTT :- 感谢您的投入,Union 似乎是一个很好的解决方案,但我认为在上述每个功能中,(您在 switch 中有“...”的地方,我必须重写代码对吗?我的意思是,你的函数中有两种类型,type1 和 type2。这是我通常想要避免的。
  • 不一定。您只需要具有独特行为的独特代码。在某些时候,您必须进行区分,但在此之前,您可以使用泛型数据类型的更广泛概念。我展示的开关仅适用于需要区分的级别。在更高级别,您可以只传递泛型类型。
  • 请注意,union 方法意味着无论何时更改联合中的任何结构,或向联合中添加新结构,通常都需要重新编译显式使用联合类型的所有内容.
  • @Jonathan Leffler -- 这是真的。 (void*) 方法允许您暂停编译器的类型安全特性。我认为这通常不是一个好主意。
  • 嗯...又编辑了一次以在联合中使用指针而不是结构...只是为了使其更加紧凑和灵活。
【解决方案2】:

是的,您可以使用void *,否则您可以使用Struct_A *Struct_B *。您需要仔细设计您的 API。通常,您最终会像bsearch()qsort() 那样使用一个或多个函数指针来执行特定于结构的操作(比较bsearch()qsort())。

请注意,遍历链表会很困难,因为下一个指针在结构中的不同偏移量处。使用包含 next 指针和指向实际结构的 void * 的固定通用列表结构可能会做得更好。

typedef struct List List;

struct List
{
    List *next;
  //List *prev;   // Doubly-linked lists
    void *data;
};

这是“侵入式”和“非侵入式”列表结构之间的区别。您现有的设计使用侵入式列表结构;修改列表中的结构以包含指针。建议的替代设计是非侵入式的;您可以在不修改结构类型的情况下创建任何结构类型的列表。

【讨论】:

    【解决方案3】:

    您可以重新定义结构如下。您必须根据类型进行大量转换才能修改所有早期功能以同时适用于两者。两者都需要使用 Struct_A *。

    struct Struct_A_s {
        int type;
        Struct_A_s *next;
        int a;
    }Struct_A;
    
    struct Struct_B_s {
        int type;
        Struct_A *next;
        int a;
        int b;
        int c;
        int d;
    }Struct_B;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-03
      • 2022-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-10
      相关资源
      最近更新 更多