我为一个我最初用于获取整数列表的类编写了一个链接列表ADT。现在我将使用相同的列表来表示字符和整数。我知道如何重写chars的代码,基本上只有两个List ADT,一个用于int,另一个用于chars。
我不想这样做,但是我想把它写成一个通用变量,以便将来我可以将这个ADT与其他代码一起使用而不用担心类型太多。
最初我用void *进入了这个,但是我遇到了一个错误,我很难理解如何修复。
typedef struct NodePtr {
void* data;
struct NodePtr*next;
struct NodePtr*prev;
} NodePtr;
typedef struct ListStruct {
NodePtr* first;
NodePtr* last;
NodePtr* current;
} ListStruct;
然后是其他一些代码和有趣的东西,然后我的插入方法插入到列表的前面:
void insert(listhndl L, void* data)
{
...inserting of the node here.
}
此代码不会抛出任何错误,但是当我运行驱动程序来测试它时:
ListHndl test = NULL;
test = newList();
insert(test, 1);
我收到错误消息:
warning: passing argument 2 of 'insert' makes pointer from integer without a cast [enabled by default]
insert(test, 1);
^
error: expected 'void *' but argument is of type 'int'
void insert(ListHndl L, void* data);
我在这里感到困惑,因为如果void *是泛型类型,怎么会抛出一个错误,说它不是预期的类型?
我做错了什么?
我在这里看到有些人建议使用枚举和联合作为泛型而不是无效*,但我无法让它工作以太,因为我不知道如何处理它们。如果有人想用enums / unions方法回答如何做泛型,我会非常感激。
答案 0 :(得分:3)
您的列表添加参数是void*
,因此您自然会被标记为隐式转换警告(如果您正在使用-Wall -Werror进行编译,则会出现错误)。 任何节点管理算法的通用实现并不像看起来那么简单,而且已经在主题上编写/编码了很多。
在您的情况下,您可以动态分配自己添加的数据(即分配int
并将结果地址作为void*
发送,或者创建列表界面函数的其他参数化并使它们成为可能聪明到足以弄明白要分配什么(如果有的话)
通常,如果您计划使用指针(并且下面链接的所有示例都有一定的长度来适应这种情况),通用链表最终不会逃避所有权和大小调整信息。一个精心设计的足够的接口能够将这些信息构建到一个足够通用的合理节点架构中,这是繁琐的,但这是你为泛型付出的代价。表演同样是一个因素,并再次付出吹笛者。对于包含不同数据类型的列表,类型信息也可能是一个考虑因素,特别是如果您倾向于使用算法来最小化内存管理使用情况。
有一个众多的通用链接列表来源于我们称之为万维网的这种宏大幻觉。以下是一些此类实现:
PseudoMuto Generic Linked List:假设所有项目大小相同,通过内部内存管理为其保留动态存储空间。
CMU Generic Linked List:关于仿制药开发的讲义。
Uncredited Generic List (circa 1999):另一种固定大小的动态管理链表。不是特别令人印象深刻,但至少是功能性的。
Atachil's Generic Linked List:另一个通用实现,这个实现为双链表。
来自的地方还有很多(谷歌和过滤掉垃圾10分钟)。
你不需要重新发明轮子,但如果你想要有很多可以帮助你的例子。一个挑战是实施一个
列表union
内部list_node
以及blob或字符串数据的void*
和size_t
,当然,一个类型标识符,以便您知道哪个成员是成员。开始你的任务时需要考虑的几件事情。祝你好运。
答案 1 :(得分:1)
您可以通过将数据封装到typedef
中来使处理的数据类型更加不透明:
typedef struct my_opaque_data {
int item; /* example opaque data */
} DATA;
struct NodePtr {
DATA data;
struct NodePtr *next, *prev;
} NodePtr;
到目前为止,我所做的只是将void *
替换为DATA
。
然后,您可能希望声明一些基本操作在其他地方实现以处理类型:
extern int data_compare (const DATA *left, const DATA *right); /* return < 0 if left < 0, 0 if equal, etc. */
extern void data_set (DATA *dest, const DATA *src); /* *dest = *src */
extern void data_swap (DATA *d1, DATA *d2); /* exchange *d1 and *d2 */
extern size_t data_size (const DATA *d); /* return size (bytes) of *d */
...
这些中的每一个几乎肯定都是微不足道的,但是通过在DATA
中替换不同的数据类型,甚至是复杂的数据类型,源代码可以保持完整的通用性并且无视DATA中的内容。有了这个,就可以轻松实现列表操作,排序,搜索等等。