在联合体中获取匿名结构体的大小

6
我希望这不是一个重复的问题,但是我已经进行了详细搜索,没有找到完全符合我的情况。
我有一个简单的结构体,我也想能够以一个简单的字节数组的方式访问它。
union
{
  struct
  {
    unsigned char a;
    unsigned char b;
    // ... Some other members ...
    unsigned char w;
  };
  unsigned char bytes[sizeof( what? )];
} myUnion;

请注意,该结构体没有名称,也没有自己的成员名称。这是为了我可以使用myUnion.a来访问该成员,而不是myUnion.myStruct.a
然而,如果没有名称,那么如何获取结构体的大小以便在更改内容时不需要手动计算呢?
我的当前解决方法是使用#define来弥补myUnion.myStruct的问题,但这会对编辑器中的自动完成造成负面影响,并使我的数据结构更难理解。
有什么好的想法吗?
注意:这是在一台8位处理器上运行的。没有字对齐等问题。尽管如此,任何注意事项都应该说明,以免其他人不当地使用提出的解决方案。

2
使用offsetof()。http://www.cplusplus.com/reference/cstddef/offsetof/。在我看来不可能得到任何错误的结果。 - this
1
这不是联合体应该被使用的方式,非常危险。如果你很幸运,编译器不会向你的结构体添加任何填充并且它会工作。 - Red Alert
1
@RedAlert 你可以“询问编译器”不要在元素之间添加填充,但是每当你看到这样的情况时,你应该假设它不会是可移植的。 - Grady Player
旁注:在标准C++中,不允许在联合体/其他类中使用匿名结构体。它们甚至在C99中也不被允许,最终C11已经添加了(官方)支持。g++和clang++支持它们作为语言扩展。 - dyp
1
我认为只需要一个普通的结构体,然后通过强制类型转换或函数调用来访问它作为“字节”将会更简单。使用非标准构造和技巧越少,以后就会遇到越少的问题。 - M.M
3个回答

7

只需摆脱联合。通过将地址强制转换为char*,您可以安全地访问任何微不足道的可复制结构作为字节数组,并且强制转换不会在读取非活动联合成员时遇到未定义行为。

struct
{
    unsigned char a;
    unsigned char b;
    // ... Some other members ...
    unsigned char w;

    // array-style access
    unsigned char& operator[](size_t i)
    { return reinterpret_cast<unsigned char*>(this)[i]; }
} myStruct;

这种类型的转换是安全的原因在于char是从严格别名限制中特殊豁免的。对于联合体,您获得的唯一特殊许可是访问“共享公共初始序列的标准布局结构体”的成员...不幸的是,数组不符合“标准布局结构体”的标准。我希望看到规则更改为“标准布局结构体或聚合”,但在当前措辞中联合版本并不安全。
在C99中,但不适用于任何版本的C++中,您可以使用灵活的数组成员而无需完全指定大小。
union
{
  struct
  {
    unsigned char a;
    unsigned char b;
    // ... Some other members ...
    unsigned char w;
  };
  unsigned char bytes[];
} myUnion;


是啊,那看起来更合理一些......甚至可以只使用一个字节数组。 - Grady Player
看起来我没有使用C99,因为当尝试第二个解决方案时出现了“联合中的可变数组成员”错误。第一个似乎需要C++的语法糖,但是基本前提应该适用于我使用#define myStructBytes ((char *)myStruct)或类似的东西。谢谢! - gkimsey
@gkimsey:我没有测试过,所以可能这是C99中你不能使用柔性数组成员的地方之一。编译器将其识别为该类型的事实非常有说服力。TonyK版本使用大小为“1”的方法是一个合理的解决方法,可能在大多数编译器上都可以工作,但我认为它确实违反了规则。 - Ben Voigt
n1570 6.7.2.1/18:“作为一个特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这被称为柔性数组成员。”因此看起来确实是非法的。(编辑:...太晚看到了编辑) - dyp

1

This will work:

union
{
  struct
  {
    unsigned char a;
    unsigned char b;
    // ... Some other members ...
    unsigned char w;
  };
  unsigned char bytes[1];
} myUnion;

我曾经考虑过这个问题,并且认为如果我在代码中的某个地方使用 myUnion.bytes[2],编译器会抱怨,但是我刚刚尝试了一下,它并没有。我想我会采用这种方法,尽管它可能不太可移植。 - gkimsey
这是一个 Ideone,使用此解决方案如果对任何人有益。 http://ideone.com/MDnXC6 - gkimsey
@gkimsey:它是100%可移植的。表达式myUnion.bytes [2]*(myUnion.bytes + 2)相同,这是明确无误的。 - TonyK

0

你无法避免给前面的匿名结构命名。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接