是否使用联合未定义行为访问结构上的特定数组索引?

时间:2019-04-02 17:52:49

标签: c unions

考虑以下包含动物类型的C结构声明

typedef enum Animals { DOG, CAT, LION, ELEPHANT, HIPPO } Animals;

typedef struct {
    union {
        struct {
            Animals pet; /*!< Pet animals are stored at index 0 */
            Animals zoo; /*!< Zoo animals are stored at index 1 */
        };
        Animals animals_list[2];
    };
} AnimalsList;

我想有时使用pet成员或zoo成员来访问动物,有时想使用animals_list对所有动物进行迭代。我期望pet总是在animals_list的索引0处,而zoo总是在animals_list的索引1处。

这个假设正确吗?可以将其视为未定义的行为吗?我特别担心跨平台的字节序差异。

我在stackoverflow上看到了关于工会的多个问题,但它们似乎都使用了不同大小的成员。我的会员人数为相同,我正在尝试保持这种方式。

2 个答案:

答案 0 :(得分:2)

  

这个假设正确吗?可以

取决于sizeof Animals。编译器可以选择在每个成员之间添加额外的填充。您可以提示编译器使用特定的填充,并使用_Static_assert(C11)确保两个大小均匹配:

_Static_assert(sizeof(animals_list) == 2 * sizeof(Animals), "Sizes don't match.")

结构成员顺序由标准指定,但offsetof(S, member)的结果不是。

尽管如此,匿名结构成员之间和animals_list项之间可能存在的填充应该完全相同,因此它很可能在任何平台上都可以使用,但是语言规范并不能保证

答案 1 :(得分:1)

根据标准,arrayLValue[index]等效于*(arrayLValue+index)。 gcc和clang都不允许访问*(someUnion.arrayMember+index)可能影响someUnion的可能性。尽管gcc和clang似乎认识到对someUnion.arrayMember[index]的访问可能会影响someUnion,但是它们唯一允许行为与假定等效的*(someUnion.arrayMember+index)有所不同的唯一方式是,如果标准没有不能定义两者的行为。