这是联盟的正确用法吗?

时间:2012-08-14 17:15:17

标签: c++ c unions

我想要命名字段而不是索引字段,但是对于某些用法,我必须迭代字段。愚蠢的简化例子:

struct named_states {float speed; float position;};

#define NSTATES (sizeof(struct named_states)/sizeof(float))
union named_or_indexed_states {
   struct named_states named;
   float indexed[NSTATES];
}
...
union named_or_indexed_states states,derivatives;
states.named.speed = 0;
states.named.position = 0;
...
derivatives.named.speed = acceleration;
derivatives.named.position= states.named.speed;
...
/* This code is in a generic library (consider nstates=NSTATES) */
for(i=0;i<nstates;i++)
    states.indexed[i] += time_step*derivatives.indexed[i];

这避免了从命名结构到索引数组的复制,反之亦然,并用通用解决方案替换它,因此更容易维护(当我增加状态向量时,我几乎没有什么地方可以改变)。它也有效以及我测试的各种编译器(gcc / g ++和MSVC的几个版本)。

但理论上,正如我所理解的那样,它并没有严格遵守正确的联合使用,因为我编写了命名字段然后读取了索引字段,我不确定我们可以说它们共享相同的结构字段......

你能否证实这是理论上不好的(非便携式)?

我应该更好地使用演员表,memcpy()还是别的什么?

除了理论之外,从实用的POV是否有任何真正的可移植性问题(一些不兼容的编译器,奇异的结构对齐,计划的演变......)?

编辑:您的答案应该更加明确我的意图:

  • 让程序员专注于领域特定的方程式,并将其从维护转换函数中释放出来(我不知道如何编写一个通用的,除了看起来不那么强大的强制转换或memcpy技巧)
  • 通过使用struct(完全由编译器控制)与数组(更多程序员错误的解密和访问)来增加更多的编码安全性。
  • 避免使用枚举或#define
  • 过多地污染命名空间

我需要知道

  • 我的转向标准是多么便携/危险(也许一些具有侵略性内联的编译器将使用完整的寄存器解决方案并避免任何内存交换破坏技巧),
  • 如果我错过了部分或全部解决上述问题的标准解决方案。

3 个答案:

答案 0 :(得分:3)

并不要求named_states中的两个字段与数组元素的排列方式相同。他们很有可能,但你在那里有编译器依赖。

这是C ++中您正在尝试做的一个简单实现:

struct named_or_indexed_states {
    named_or_indexed_states() : speed(indexed[0], position(indexed[1]) { }
    float &speed;
    float &position;
    float indexed[2];
};

如果由于引用元素而导致的大小增加太多,请使用访问器:

struct named_or_indexed_states {
    float indexed[2];
    float& speed() { return indexed[0]; }
    float& position() { return indexed[1]; }
};

编译器在内联访问器时没有问题,因此读取或写入speed()position()的速度与成员数据一样快。不过,你仍然需要编写令人讨厌的括号。

答案 1 :(得分:2)

只有访问最后一个联合书面成员才能明确定义;您提供的代码使用,就标准C(或C ++)而言,未定义的行为 - 它可能有效,但这是错误的方法。结构使用与数组类型相同的类型并不重要 - 可能涉及填充,以及编译器使用的其他不可见技巧。

一些编译器,如GCC,确实将其定义为实现类型惩罚的允许方式。现在问题出现了 - 我们是在谈论标准C(或C ++),还是GNU或任何其他扩展?

至于你应该使用什么 - 适当的转换运算符和/或构造函数。

答案 2 :(得分:1)

这可能有点过时,但在这种情况下我会做的是:

枚举 {

F_POSITION,

F_SPEED,

F_COUNT };

浮动状态[F_COUNT];

然后您可以将它们引用为: 状态[F_POSITION]和状态[F_SPEED]。

这是我写这篇文章的一种方式。我确信还有很多其他可能性。