繁体   English   中英

C中的通用列表使用void指针

[英]Generic List in C using void pointer

我正在尝试创建一个允许输入任何类型的通用列表。 但是,我遇到了is_element_of函数中的比较问题(因为我正在使用void指针)。 有帮助吗?

 typedef struct Item{ 
            void* data;
        } Item;

    typedef struct Node{
        Item Item; 
        struct Node* next; 
        struct Node* previous;
    } Node;

    typedef Node* List;

bool is_element_of(Item Item, List *pointertolist) {
    bool isinlist = false;
    Node *scan = *pointertolist; 
    while (scan->next != NULL) { 
        if ((scan->Item.data) == (Item.data)) {
            printf("Match!");
            isinlist = true;
        } else {
            printf("No Match!");
            isinlist = false;
        }
        scan = scan->next; 
    }

    return isinlist;
}

您需要将类型感知操作委托给单独的函数,然后通过函数指针将该函数附加到列表中。 我冒昧地将你的结构类型定义更改为我之前使用过的东西,我觉得它更自然一些。 随意不同意。

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

除非你打算让Item结构类型保存除void *以外的任何东西,否则就去掉它。 抽象是一件好事,但有一种事情就是过分了。 就个人而言,我认为创建一堆typedef更清晰,我真的不喜欢typedef'ing指针类型(指针语义是特殊的,不应该被隐藏)。 当然,YMMV。

struct List {
  struct Node *head;
  struct Node *tail;
  int (*cmp)( const void *, const void *);
};

我已经修改了List类型以包含头部和尾部指针,以及指向比较函数的指针。 您可以使用此结构来创建任何类型的列表; 您需要做的就是为该类型创建一个比较函数并将其附加到列表中。 例如,如果您希望列表包含整数:

int compareInt( const void *lhs, const void *rhs )
{
  const int *llhs = (const int *) lhs;  
  const int *lhrs = (const int *) rhs;  

  if ( *llhs < *lrhs )                  
    return -1;
  else if ( *llhs > *lrhs )
    return 1;

  return 0;
}

比较函数遵循与qsort使用的模型相同的模型; 它需要两个const void *参数并返回一个int值 - 如果lhs <rhs则为-1,如果lhs> rhs则为1,如果lhs == rhs则为0。

struct List intList = { NULL,  NULL, compareInt };

bool contains( const Item data, struct List *l ) 
{
  bool result = false;

  assert( l != NULL && l->cmp != NULL);

  struct Node *cur = l->head;

  while ( cur != NULL )
  {
    if ( l->cmp( cur->data, data ) == 0 )
    {
      result = true;
      break;
    }
    cur = cur->next;
  }

  return result;
}

因此,您的函数将遍历列表并比较值,如果找到匹配则返回true,否则返回false。

现在,这种方法有很多缺点; 它很复杂,很乱,很难遵循,它可能涉及大量的内存管理,你失去了任何类型安全的借口。 没有什么可以阻止您将错误的功能与列表相关联,或者混合列表中的类型。 但是,它确实允许您创建可以处理任何类型的“通用”容器,并且您可以添加新类型而无需破解基本容器逻辑。 您只需要为每种类型实现一个新的比较函数。

虽然,老实说,您不仅应该实现比较函数,还应该实现赋值,复制,显示和释放函数,以相同的方式将它们附加到列表中,以及每种类型的轻量级,类型感知的接口。 这是更多的代码,但从长远来看,它将为您节省胃灼热。

您应该在开始时将isInList设置为false,并在找到匹配项时将其标记为true。 然后终止循环:

typedef struct Item
{
   void* data;
} Item;

typedef struct Node
{
   Item Item;
   struct Node* next;
   struct Node* previous;
} Node;

typedef Node* List;

bool is_element_of(Item Item, List *pointertolist)
{
   bool isInList = false;
   Node *scan = *pointertolist;
   while (scan != NULL && !isInList)
   {
      if ((scan->Item.data) == (Item.data))
      {
         printf("Match!\n");
         isInList = true;
      }
      scan = scan->next;
   }
   if(!isInList)
      printf("No Match!\n");
   return isInList;
}

测试功能:

void testit()
{
   Node n1;
   Node n2;
   Node n3;

   Item item;


   n1.Item.data = (void*)0x23;
   n2.Item.data = (void*)0x24;
   n3.Item.data = (void*)0x25;

   n1.next = &n2;
   n2.next = &n3;
   n3.next = NULL;

   List list = &n1;

   item.data = (void*)0x23;
   is_element_of(item, &list);

   item.data = (void*)0x24;
   is_element_of(item, &list);

   item.data = (void*)0x25;
   is_element_of(item, &list);

   item.data = (void*)0x26;
   is_element_of(item, &list);

}

输出结果:

Match!
Match!
Match!
No Match!

您不能取消引用void *并比较该值,因为它可以是任何大小(例如:您如何比较int和short?

没有更多的数据知识(是的,它总是关于输入数据;-),很难给出更明确的答案。 但这里有两条你可以追求的路径......

[edit]我假设您的列表可能包含同一列表中任何类型的数据。 人们经常使用void *因为他们想要将不同类型链接在一起。

简短的(坏的)答案

你必须在比较值或比较指针之间做出选择。 如果您不介意只比较指针,那么您可以通过为其指定数据地址来设置项目:

node.Item.data =  &myval;

在这种情况下,您可以查看是否已将此位置在ram中添加到节点中。 但是,这不允许您在应用中的两个不同位置比较相同的值。 因此,如果下面的x和y具有相同的值,则无法比较您有两个指向1的节点。

x = 1;
y = 1;
node.Item.data = &x;
node2.Item.data = &y;

此外,如果您添加了在堆栈上分配的任何节点,您将很快将自己铲入坟墓(因为节点最终可能会引用在堆栈上无效的地址)!

更长(更好)的答案

当我有这样的通用累加器系统时,我使用了一个联合而不是使用void *。

enum {
    tp_int =    0x0001,
    tp_short =  0x0002,
    tp_vptr =   0x0004, // void pointer
    tp_sptr =   0x0008  // string pointer (whatever type you use 
                        // for your strings... ? char *)
    // ... add other types, including structs you may want to compare...
};

typedef struct Item {
    int type;   // <- essential!
    union data {
        int   i;
        short s;
        void *ptr; // all pointer types can use the same, unless you want to compare 
                   // compound values (a struct member?) where you'd then use the 
                   // tp_xxx to properly select which
                   // comparison to use ... as below.
    };
} Item;


typedef struct Node{
    Item Item; 
    struct Node* next; 
    struct Node* previous;
} Node;

typedef Node* List;

bool is_element_of(Item Item, List *pointertolist) {
    bool isinlist = false;
    Node *scan = *pointertolist; 
    while (scan->next != NULL) {
        //isinlist = false;
        if (scan->Item.type == Item.type){
            if (Item.type & (tp_vptr | tp_sptr)){
                // compare pointer types
                if ((scan->Item.ptr) == (Item.ptr)){
                    printf("pointer Match!");
                    isinlist = true;
                    break;
                }
            } else if (Item.type == tp_int){
                // compare integers (4 bytes?)
                if ((void *)(scan->Item.i) == (void *)(Item.i)){
                    printf("integer Match!");
                    isinlist = true;
                    break;
                }
            } else if (Item.type == tp_short){
                // compare shorts (2 bytes?)
                if ((scan->Item.s) == (Item.s)){
                    printf("short Match!");
                    isinlist = true;
                    break;
                }
            }
        } 
        scan = scan->next; 
    }

    return isinlist;
}

注意上面的代码中可能有一两个奇怪的错误,我现在还没有设置编译它。

您可以在此处选择要使用的比较。 它还允许您正确安全地使用值类型(如整数和短路)。

如果你有更复杂的结构,你可以使用他们的数据而不是指针轻松地比较它们,这样你就可以看到你是否有相同的数据。

你甚至可以扩展上面的内容来检查类似的字符串,即使它们有不同的内存位置。 (当Item.type是tp_sptr :-)时

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM