Ori*_*ski 9 c struct data-structures
假设我有这个struct(偶然包含位字段,但你不应该在意):
struct Element {
unsigned int a1 : 1;
unsigned int a2 : 1;
...
unsigned int an : 1;
};
Run Code Online (Sandbox Code Playgroud)
我想以方便的方式访问第i个成员.我们来检查一下检索解决方案.
我想出了这个功能:
int getval(struct Element *ep, int n)
{
int val;
switch(n) {
case 1: val = ep->a1; break;
case 2: val = ep->a2; break;
...
case n: val = ep->an; break;
}
return val;
}
Run Code Online (Sandbox Code Playgroud)
但我怀疑有一个更简单的解决方案.也许是数组访问风格之类的东西.
我试着这样做:
#define getval(s,n) s.a##n
Run Code Online (Sandbox Code Playgroud)
但预计它不起作用.
有更好的解决方案吗?
Jar*_*Par 13
除非你对结构的底层结构有特定的了解,否则无法在C中实现这样的方法.会遇到各种各样的问题,包括
你最好亲自为你的结构实现一个方法,它对结构的内部成员有深刻的理解.
如果结构中的每个字段都是一个int,那么你基本上应该可以说
int getval(struct Element *ep, int n)
{
return *(((int*)ep) + n);
}
Run Code Online (Sandbox Code Playgroud)
如果是整数,则将指向结构的指针转换为指向数组的指针,然后访问该数组的第n个元素.由于结构中的所有内容似乎都是整数,因此这是完全有效的.请注意,如果你有一个非int成员,这将失败.
更通用的解决方案是维护一个字段偏移数组:
int offsets[3];
void initOffsets()
{
struct Element e;
offsets[0] = (int)&e.x - (int)&e;
offsets[1] = (int)&e.y - (int)&e;
offsets[2] = (int)&e.z - (int)&e;
}
int getval(struct Element *ep, int n)
{
return *((int*)((int)ep+offsets[n]));
}
Run Code Online (Sandbox Code Playgroud)
这将在你能够调用结构getval的任何int字段的意义上工作,即使你的结构中有其他非int字段,因为偏移量都是正确的.但是,如果您尝试调用getval其中一个非int字段,则会返回完全错误的值.
当然,您可以为每种数据类型编写不同的函数,例如
double getDoubleVal(struct Element *ep, int n)
{
return *((double*)((int)ep+offsets[n]));
}
Run Code Online (Sandbox Code Playgroud)
然后只需为您想要的任何数据类型调用正确的函数.顺便说一句,如果你使用C++,你可以说类似的东西
template<typename T>
T getval(struct Element *ep, int n)
{
return *((T*)((int)ep+offsets[n]));
}
Run Code Online (Sandbox Code Playgroud)
然后它适用于您想要的任何数据类型.
如果你的结构是除了位域之外的任何东西,你可以只使用数组访问,如果我正确地记住C保证结构的一系列成员都是相同类型,具有与数组相同的布局.如果您知道编译器将位域按什么顺序存储到整数类型中,那么您可以使用shift/mask ops,但那是依赖于实现的.
如果要通过变量索引访问位,那么最好用包含标志位的整数替换位域.变量访问实际上不是bitfields的用途:a1 ... an基本上是独立的成员,而不是位数组.
你可以这样做:
struct Element {
unsigned int a1 : 1;
unsigned int a2 : 1;
...
unsigned int an : 1;
};
typedef unsigned int (*get_fn)(const struct Element*);
#define DEFINE_GETTER(ARG) \
unsigned int getter_##ARG (const struct Element *ep) { \
return ep-> a##ARG ; \
}
DEFINE_GETTER(1);
DEFINE_GETTER(2);
...
DEFINE_GETTER(N);
get_fn jump_table[n] = { getter_1, getter_2, ... getter_n};
int getval(struct Element *ep, int n) {
return jump_table[n-1](ep);
}
Run Code Online (Sandbox Code Playgroud)
并且可以通过多次包含相同标头的技巧来避免一些重复,每次都以不同方式定义宏.标题会为每个1 ... N扩展一次该宏.
但我不相信这是值得的.
它确实处理了JaredPar的观点,即如果你的结构混合了不同的类型,你会遇到麻烦 - 这里通过特定跳转表访问的所有成员当然必须属于同一类型,但它们之间可能有任何旧的垃圾.尽管如此,这仍然留下了JaredPar的其余部分,而且与交换机相比,这是一个很大的代码膨胀.