【发布时间】:2010-09-09 05:28:57
【问题描述】:
我有一些具有多个功能非常相似的代码,用于根据结构中一个字段的内容在列表中查找项目。函数之间的唯一区别是发生查找的结构的类型。如果我可以传入类型,我可以删除所有代码重复。
我还注意到这些函数中也发生了一些互斥锁,所以我想我可以不理会它们......
【问题讨论】:
标签: c
我有一些具有多个功能非常相似的代码,用于根据结构中一个字段的内容在列表中查找项目。函数之间的唯一区别是发生查找的结构的类型。如果我可以传入类型,我可以删除所有代码重复。
我还注意到这些函数中也发生了一些互斥锁,所以我想我可以不理会它们......
【问题讨论】:
标签: c
如果您确保该字段被放置在每个此类结构中的相同位置,您可以简单地转换一个指针来获取该字段。这种技术用于许多低级系统库,例如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;
}
这样做会失去类型安全性,但这是一种很有价值的技术。
[我现在已经实际测试了上面的代码并修复了各种小错误。有了编译器就容易多了。 ]
【讨论】:
char 项目不会被放置在偏移量 0 以外的任何位置。如果该字段出现在其他任何位置,则可能会出现问题.
由于结构只不过是预定义的内存块,因此您可以这样做。您可以将 void * 传递给结构,并传递一个整数或其他东西来定义类型。
从那里开始,最安全的做法是在访问数据之前将 void * 重铸为适当类型的指针。
您需要非常非常小心,因为当您转换为 void * 时会失去类型安全性,并且在执行此类操作时可能会遇到难以调试的运行时错误。
【讨论】:
我认为您应该查看 C 标准函数 qsort() 和 bsearch() 以获得灵感。这些是对数组进行排序和在预排序数组中搜索数据的通用代码。它们适用于任何类型的数据结构 - 但您将指针传递给进行比较的辅助函数。辅助函数知道结构的细节,因此可以正确进行比较。
事实上,由于您想要进行搜索,可能只需要 bsearch(),但如果您正在动态构建数据结构,您可能会决定您需要与排序列表不同的结构. (您可以使用排序列表——与堆相比,它只会减慢速度。但是,您需要一个通用的 heap_search() 函数和一个 heap_insert() 函数才能正确完成这项工作,并且此类函数在 C 中未标准化。搜索网络显示此类函数存在 - 不是那个名称;只是不要尝试“c heap search”,因为它假定您的意思是“廉价搜索”并且您会得到大量垃圾!)
【讨论】:
如果您测试的 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 的要求——在调用站点传递结构的类型:
#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;
}
【讨论】:
这样做的一种方法是将类型字段作为结构的第一个字节。您的接收函数查看此字节,然后根据它发现的内容将指针转换为正确的类型。另一种方法是将类型信息作为单独的参数传递给需要它的每个函数。
【讨论】:
您可以使用参数化宏来执行此操作,但大多数编码策略会对此表示不满。
#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));
}
【讨论】:
简短的回答:不。但是,您可以为此创建自己的方法,即提供有关如何创建此类结构的规范。但是,这通常不是必需的,也不值得付出努力;只是通过引用。 (callFuncWithInputThenOutput(input, &struct.output);)
【讨论】:
我对 c 有点生疏,但尝试使用 void* 指针作为函数参数中的变量类型。然后将结构的地址传递给函数,然后按照您的方式使用它。
void foo(void* obj);
void main()
{
struct bla obj;
...
foo(&obj);
...
}
void foo(void* obj)
{
printf(obj -> x, "%s")
}
【讨论】: