我在C中为列表实现简单库,我在编写find
函数时遇到问题。
我希望我的函数接受任何类型的参数来查找,两者:
find(my_list, 3)
和find(my_list, my_int_var_to_find)
。
我已经掌握了列表元素类型的信息。
目前我已经找到了几种解决方法:
带有不同类型后缀的不同函数:int findi(void* list, int i)
,int findd(void* list, double d)
- 但我不喜欢这种方法,对我来说似乎是冗余,API也很混乱。
使用union:
typedef union {
int i;
double d;
char c;
...
} any_type;
但是这样我强迫用户了解any_type
union,并在调用find
之前创建它。我想避免这种情况。
使用可变参数函数:int find(void* list, ...)
。我喜欢这种方法。但是,我担心没有对参数数量的限制。用户可以自由写int x = find(list, 1, 2.0, 'c')
,但我不知道它应该是什么意思。
我也看到了这个问题的回答:C : send different structures for one function argument但它无关紧要,因为我想接受非指针参数。
处理此功能的正确方法是什么?
答案 0 :(得分:7)
您可以尝试实现类似于bsearch
等通用函数的函数,它可以对任何数据类型的数组执行二进制搜索:
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *))
不是硬编码函数内不同数据类型的不同实现,而是将指针传递给将执行类型相关操作的函数,并且只知道底层实现。在您的情况下,这可能是某种遍历/迭代函数。
bsearch
需要知道的另一件事(除了显而易见的 - 搜索键和数组长度)是数组中每个元素的大小,因此它可以计算数组中每个元素的地址,将它传递给比较函数。
如果你有一个有限的类型列表,那么有一系列findX()
函数就没有错。上述方法需要将每种数据类型的函数传递给bsearch
函数,但其中一个主要区别是不需要重复常用功能,并且通用函数可用于任何数据类型。
我不会说有任何正确的方法可以做到这一点,这取决于你并且真正取决于你想要解决的问题。
答案 1 :(得分:1)
我不确定回答我自己的问题是否礼貌,但我想要你的意见。
我尝试使用va_list解决此问题。为什么这样?因为这样我只能编写一个函数。请注意,我知道论证应该是什么类型。这样我就可以这样做:
int find(void* list, ...) {
any_type object = {0};
int i = -1;
va_list args;
va_start(args, list);
switch(type_of_elem(list)) {
case INT: object.i = va_arg(args, int); break;
...
}
/* now &object is pointer to memory ready for comparision
* f.eg. using memcmp */
return i;
}
这个解决方案的优点是我可以包装所提交的switch-case并将其重用于其他功能。
在研究了一些关于对参数数量没有限制的担忧后,我意识到printf
也没有这个限制。你可以写printf("%d", 1, 2, 3)
。
但我用额外的宏调整了我的解决方案:
#define find_(list, object) find((list), (object))
在编译时会产生错误消息,说find_ macro expects 2 arguments not 3
。
你怎么看?你认为这是比以前建议更好的解决方案吗?