4

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

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 技巧)
  • 通过使用结构(由编译器完全控制)与数组(声明和访问受更多程序员错误影响)来增加更多的编码安全性
  • 避免使用 enum 或 #define 过多地污染命名空间

我需要知道

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

3 回答 3

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()成员数据一样快。但是,您仍然必须编写那些烦人的括号。

于 2012-08-14T17:26:36.387 回答
2

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

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

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

于 2012-08-14T17:21:02.463 回答
1

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

枚举{

F_POSITION,

F_SPEED,

F_COUNT };

浮动状态[F_COUNT];

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

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

于 2012-08-14T17:29:11.393 回答