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

时间:2013-12-23 13:11:58

标签: c list function generics void-pointers

我正在尝试创建一个允许输入任何类型的通用列表。但是,我遇到了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;
}

3 个答案:

答案 0 :(得分:1)

您应该在开始时将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!

答案 1 :(得分:1)

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

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

除非您打算让Item结构类型保存除void *以外的任何内容,否则请删除它。抽象是一件好事,但有一种事情就是过分了。就个人而言,我认为创建一堆typedef更清晰,我真的不喜欢typedef'指针类型(指针语义是特殊的,不应该被隐藏)。当然,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&lt;则返回-1。 rhs,1如果lhs> rhs,如果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。

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

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

答案 2 :(得分:0)

你不能取消引用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时: - )