2011-04-02 44 views
4

我有两个结构,与计算出一个沉吟平均,这样的简化版本值:访问结构成员就好像它们是单个数组?

typedef struct 
{ 
    int v_move, v_read, v_suck, v_flush, v_nop, v_call; 
} values; 

typedef struct 
{ 
    int qtt_move, qtt_read, qtt_suck, qtd_flush, qtd_nop, qtt_call; 
} quantities; 

然后我用它们来计算:

average = v_move*qtt_move + v_read*qtt_read + v_suck*qtt_suck + v_flush*qtd_flush + v_nop*qtd_nop + v_call*qtt_call; 

每一个现在,他们我需要包含另一个变量。现在,比如,我需要包括v_cleanqtt_clean。我不能改变结构数组:

typedef struct 
{ 
    int v[6]; 
} values; 
typedef struct 
{ 
    int qtt[6]; 
} quantities; 

这将简化了很多我的工作,但他们需要的变量名是明确的API的一部分。

所以,我正在寻找一种方法来访问该结构的成员,也许使用sizeof(),所以我可以把它们当作一个数组,但仍然保持API不可更改。这是保证所有的值都是int,但我不能保证一个int的大小。

写作的问题来到我的脑海......一个union能胜任这项工作?是否有另一个聪明的方法来自动添加另一个成员的任务?

感谢, 贝乔

+0

为了更好地帮助你,我们应该知道api是怎样的... – digEmAll 2011-04-02 17:05:09

+0

@digEmAll从外部开始,应该让程序员保持旧代码的运行。该代码使用如下行:v.v_move = 1。这不能打破。 – 2011-04-02 17:08:58

+0

有点复杂,但你可以迭代结构域...看看[这个答案](http://stackoverflow.com/questions/1784782/is-there-any-way-to-loop-through-a-struct -with-elements-of-diferent-types-in-c/1785699#1785699) – digEmAll 2011-04-02 17:19:20

回答

8

如果所有成员保证是类型int您可以使用指针来诠释并加:

int *value = &(values.v_move); 
int *quantity = &(quantities.qtt_move); 
int i; 
average = 0; 
// although it should work, a good practice many times IMHO is to add a null as the last member in struct and change the condition to quantity[i] != null. 
for (i = 0; i < sizeof(quantities)/sizeof(*quantity); i++) 
    average += values[i] * quantity[i]; 

(由于成员在一个结构的顺序是保证是所申报)

+0

是否保证字段之间没有填充?在这种情况下听起来是合理的,只是想知道。 – Mat 2011-04-02 17:08:42

+0

@Mat:对不起,什么是填充?我认为这个解决方案看起来很有希 – 2011-04-02 17:12:34

+0

@Mat - 有趣的问题。我认为在这种情况下填充不会改变,因为指针应该分别增加。 – MByD 2011-04-02 17:14:38

4

这真的听起来好像这应该是因为beggining阵列,存取方法或宏使您还在用漂亮的名字,如移动,阅读等。但是,当你提到,由于API破损,这是不可行的。

两个解决方案,来我的脑海中:

  • 使用编译器特定的指令,以确保您的结构包装(因此,它转换为数组是安全的)
  • 邪恶宏黑魔法。
+0

大声笑'邪恶的宏黑魔法'。 – 2011-04-02 17:29:16

+0

+1博士是否有ascii表情符号。邪恶的婴儿手指? :) – user237419 2011-04-02 17:50:49

2

这个问题很常见,而在过去的很多方面已经得到解决。他们都不是完全安全或干净的。这取决于你的特殊应用。以下是可能的解决方案列表:

1)你可以重新定义你的结构,从而字段将成为数组元素,并使用宏来每个特定元素映射,如果它是一个结构域。例如:

struct values { varray[6]; }; 
#define v_read varray[1] 

这种方法的缺点是大多数调试器不理解宏。另一个问题是,理论上编译器可能会为原始结构和重新定义的结构选择不同的对齐方式,因此二进制兼容性不能保证。

2)指望编译器的行为,并把所有的领域,因为它,他们阵场(哎呀,我在写这一段时间,别人写的一样 - +1他)

3)创建元素偏移量的静态数组(在启动时初始化)并使用它们“映射”元素。这是非常棘手的,并没有那么快,但具有独立于结构中场的实际布置的优点。示例(不完整,只是为了说明):

int positions[10]; 
position[0] = ((char *)(&((values*)NULL)->v_move)-(char *)NULL); 
position[1] = ((char *)(&((values*)NULL)->v_read)-(char *)NULL); 
//... 
values *v = ...; 
int vread; 
vread = *(int *)(((char *)v)+position[1]); 

好的,一点也不简单。像“抵消”这样的宏可能有助于这种情况。

+0

谢谢@Giuseppe - 我爷爷的名字! ;) – 2011-04-02 17:56:49

+0

看起来你的第三个解决方案与@AndreyT给出的解决方案有关,但是采用了不同的语法。 – 2011-04-02 18:04:59

+0

是的。我不得不更加一致,因为我想很快报告最常见的解决方案,而不是深入描述。 AndreyT的回答更加详细。 – 2011-04-02 22:28:25

15

你想要做的是不可能做任何优雅的方式。不可能以数组的形式可靠地访问连续的结构成员。目前接受的答案是黑客,而不是解决方案。

正确的解决方案是切换到一个数组,无论它需要多少工作。如果你使用枚举常量来进行数组索引(就像在他现在被删除的答案中建议的那样@digEmAll),那么名字和代码就会像现在一样清晰。

如果你仍然不想或不能切换到数组,唯一或多或少可接受的方式来做你想做的事情就是创建一个“索引数组”或“地图 - 阵列“(见下文)。 C++有一个专用的语言特性,可以帮助人们优雅地实现它 - 指向成员的指针。在C你被迫效仿使用C++功能offsetof宏观

static const size_t values_offsets[] = { 
    offsetof(values, v_move), 
    offsetof(values, v_read), 
    offsetof(values, v_suck), 
    /* and so on */ 
}; 

static const size_t quantities_offsets[] = { 
    offsetof(quantities, qtt_move), 
    offsetof(quantities, qtt_read), 
    offsetof(quantities, qtt_suck), 
    /* and so on */ 
}; 

如果你现在正在给

values v; 
quantities q; 

和指数

int i; 

您可以生成指向各个领域as

int *pvalue = (int *) ((char *) &v + values_offsets[i]); 
int *pquantity = (int *) ((char *) &q + quantities_offsets[i]); 

*pvalue += *pquantity; 

当然,您现在可以以任何您想要的方式遍历i。这还远没有优雅,但至少它具有一定程度的可靠性和有效性,而不是任何丑恶的黑客。通过将重复的部分包装到适当的名称函数/宏中,可以使整个事物看起来更加优雅。

+0

Hi @AndreyT这真的是一个很深的答案。谢谢!为了简单起见,我会坚持这个丑陋的黑客,但我会以正确的方式教导我的学生。 :D我假设你使用单个偏移量,因为不能保证'int * value =&(values.v_move);'和'value [2]'指向第二个成员?是这样吗?即使只有结构中的整数?谢谢(顺便说一句:+1!) – 2011-04-02 18:01:51

+0

你认为@Jason的'__attribute__((packed))'建议是否美化@MByD所显示的丑陋黑客?我的意思是,填充和对齐问题能解决吗?请记住,它是一个仅由int组成的结构,即使我不知道给定机器中的int的大小,sizeof **也是如此。感谢您的输入。您可以通过删除的答案看到 – 2011-04-02 21:03:49

+1

! :O Eheh,我删除了它,因为OP请求是为了保持API兼容性......无论如何,我同意这种解决方案比接受的更好:) – digEmAll 2011-04-03 09:30:34

1

如果您使用gcc,如何使用__attribute__((packed))

所以,你可以宣布你的结构为:

typedef struct 
{ 
    int v_move, v_read, v_suck, v_flush, v_nop, v_call; 
} __attribute__((packed)) values; 

typedef struct 
{ 
    int qtt_move, qtt_read, qtt_suck, qtd_flush, qtd_nop, qtt_call; 
} __attribute__((packed)) quantities; 

根据GCC手册,您的结构,然后将使用的内存可能的最低金额为存储结构,省略可能已经被正常地存在任何填充。然后唯一的问题就是在你的平台上确定sizeof(int),这可以通过一些编译宏或者使用<stdint.h>来完成。

还有一件事是,当需要访问并重新存储到内存中时,会对解包和重新打包结构造成性能损失。但至少你可以保证布局是一致的,并且可以像使用一个数组一样对数组进行访问,就像你想要的一样,也可以像访问数组那样访问它(也就是说,你不必担心填充会弄乱指针偏移量)。

感谢,

杰森

+0

@Jason:听起来不错。我会更详细地研究它。这是便携式吗?谢谢。 – 2011-04-02 20:26:47

+0

+1好的提示补充了答案。只要正确的语法,因为** typedef **是'typedef struct {int v_move; ...} __attribute__((packed))values;'。我只是不确定这是否便携,因为该程序运行在多个操作系统中。谢谢 – 2011-04-02 21:00:04

+0

谢谢,语法现在是固定的...就可移植性而言,我已经能够将这种类型的代码移植到gcc支持的任何平台上。但正如我在我的文章中提到的,您可能需要使用一些编译器宏等来确保您获得了针对该特定平台的sizeof(int)正确的值。 – Jason 2011-04-02 22:04:35

4

Writing the question came to my mind... Can a union do the job? Is there another clever way to automatize the task of adding another member?

是的,union当然可以做的工作:

union 
{ 
    values v; /* As defined by OP */ 
    int array[6]; 
} u; 

您可以在API中使用指针来u.values,并与u.array工作在你的代码中。

就我个人而言,我认为所有其他答案都打破了的规则,至少有惊喜。当我看到一个普通的结构定义时,我假定结构将使用普通的访问方法进行访问。通过union,很明显,应用程序将以特殊方式访问它,这提示我要特别注意代码。

+1

我越是研究这个问题,我越发现这个解决方案。 – 2017-10-16 03:31:52

相关问题