我有一些代码具有彼此非常相似的多个函数,可以根据结构中一个字段的内容查找列表中的项目。函数之间的唯一区别是查找结构的类型。如果我可以传入类型,我可以删除所有代码重复。
我也注意到在这些函数中也发生了一些互斥锁定,所以我想我可能会把它们留下来......
答案 0 :(得分:6)
如果确保将字段放置在每个此类结构中的相同位置,则可以简单地转换指针以获取字段。该技术用于许多低级系统库,例如BSD插座。
struct person {
int index;
};
struct clown {
int index;
char *hat;
};
/* we're not going to define a firetruck here */
struct firetruck;
struct fireman {
int index;
struct firetruck *truck;
};
int getindexof(struct person *who)
{
return who->index;
}
int main(int argc, char *argv[])
{
struct fireman sam;
/* somehow sam gets initialised */
sam.index = 5;
int index = getindexof((struct person *) &sam);
printf("Sam's index is %d\n", index);
return 0;
}
这样做会失去类型安全性,但这是一种有价值的技术。
[我现在实际测试了上面的代码并修复了各种小错误。有了编译器,这会容易得多。 ]
答案 1 :(得分:3)
由于结构只不过是预定义的内存块,因此您可以这样做。您可以将void *传递给结构,并使用整数或其他东西来定义类型。
从那里,最安全的要做的事情是在访问数据之前将void *重新转换为适当类型的指针。
您需要非常非常小心,因为当您转换为void *时会失去类型安全性,并且在执行此类操作时可能最终会遇到难以调试的运行时错误。
答案 2 :(得分:1)
我认为你应该看看C标准函数qsort()和bsearch()的灵感。这些是用于对数组进行排序以及在预排序数组中搜索数据的通用代码。它们可以处理任何类型的数据结构 - 但是您可以向它们传递指向执行比较的辅助函数的指针。辅助函数知道结构的细节,因此可以正确地进行比较。
事实上,由于你想要进行搜索,可能你需要的只是bsearch(),但是如果你动态构建数据结构,你可能会认为你需要一个不同于排序列表的结构。 (您可以使用排序列表 - 与堆相比,它只会减慢速度。但是,您需要一个通用的heap_search()函数和一个heap_insert()函数才能正常工作,并且这些功能在C中没有标准化。搜索网络显示存在这样的功能 - 不是通过那个名称;只是不要尝试“c堆搜索”,因为假设你的意思是“廉价搜索”,你得到大量的垃圾!)
答案 3 :(得分:1)
如果您测试的ID字段是所有结构共享的公共初始字段序列的一部分,那么使用联合保证访问将起作用:
#include <stdio.h>
typedef struct
{
int id;
int junk1;
} Foo;
typedef struct
{
int id;
long junk2;
} Bar;
typedef union
{
struct
{
int id;
} common;
Foo foo;
Bar bar;
} U;
int matches(const U *candidate, int wanted)
{
return candidate->common.id == wanted;
}
int main(void)
{
Foo f = { 23, 0 };
Bar b = { 42, 0 };
U fu;
U bu;
fu.foo = f;
bu.bar = b;
puts(matches(&fu, 23) ? "true" : "false");
puts(matches(&bu, 42) ? "true" : "false");
return 0;
}
如果你运气不好,并且该字段出现在各种结构中的不同偏移处,则可以为函数添加偏移参数。然后,offsetof和一个包装器宏模拟OP要求的内容 - 在调用站点传递struct的类型:
#include <stddef.h>
#include <stdio.h>
typedef struct
{
int id;
int junk1;
} Foo;
typedef struct
{
int junk2;
int id;
} Bar;
int matches(const void* candidate, size_t idOffset, int wanted)
{
return *(int*)((const unsigned char*)candidate + idOffset) == wanted;
}
#define MATCHES(type, candidate, wanted) matches(candidate, offsetof(type, id), wanted)
int main(void)
{
Foo f = { 23, 0 };
Bar b = { 0, 42 };
puts(MATCHES(Foo, &f, 23) ? "true" : "false");
puts(MATCHES(Bar, &b, 42) ? "true" : "false");
return 0;
}
答案 4 :(得分:0)
执行此操作的一种方法是将类型字段作为结构的第一个字节。您的接收函数会查看此字节,然后根据发现的内容将指针强制转换为正确的类型。另一种方法是将类型信息作为单独的参数传递给需要它的每个函数。
答案 5 :(得分:0)
您可以使用参数化宏执行此操作,但大多数编码策略都会对此不满意。
#include
#define getfield(s, name) ((s).name)
typedef struct{
int x;
}Bob;
typedef struct{
int y;
}Fred;
int main(int argc, char**argv){
Bob b;
b.x=6;
Fred f;
f.y=7;
printf("%d, %d\n", getfield(b, x), getfield(f, y));
}
答案 6 :(得分:0)
简答:不。但是,您可以创建自己的方法,即提供如何创建此类结构的规范。但是,这通常没有必要,也不值得付出努力;只是通过引用传递。 (callFuncWithInputThenOutput(input, &struct.output);
)
答案 7 :(得分:-1)
我在c上有点生疏,但尝试在函数参数中使用void *指针作为变量类型。然后将结构的地址传递给函数,然后按照自己的方式使用它。
void foo(void* obj);
void main()
{
struct bla obj;
...
foo(&obj);
...
}
void foo(void* obj)
{
printf(obj -> x, "%s")
}