问题 在所有系统中,C联合中的变量对齐是否相同?


考虑以下联合:

typedef union {
    struct { // Anonymous struct
        int red;
        int green;
        int blue;
    };
    int colorChannels[3];
} Color;

无论系统编译或执行此代码的系统如何,匿名结构中的字段是否总是与colorChannels中的索引对齐?

换句话说,规范是否需要内存地址 myColor.colorChannels[0] 与地址相同 myColor.red

另外,如果代码编译为C ++而不是C,答案会有所不同吗?

请注意,我问的是联合中每个元素具有相同类型/大小的具体情况(即 int 在这种情况下)


2443
2018-02-22 23:01


起源

答案是“通常,但不是必要的”。您可能想要添加静态断言。请注意,在C ++类型中,通过这样的联合进行惩罚是未定义的行为。 - HolyBlackCat
不。并且很难理解为什么你会关心它们,除非你使用这种对齐方式进行一些非常便携的转换滑稽动作。 - Neil Butterworth
注意:允许编译器在成员之间添加填充,例如,以支持有利于处理器寻址的对齐。 - Thomas Matthews
@EKW这不是用C ++实现这一目标的方法。 - Baum mit Augen
那你为什么把它标记为C ++?我刚把它删除了。 - Neil Butterworth


答案:


无论系统编译或执行此代码的系统如何,匿名结构中的字段是否总是与colorChannels中的索引对齐?

不必要。该实现可以在单个变量之间添加字节“填充” struct。 “填充”只是在变量之间或之后插入到内存定义中的额外字节的内存 struct 以实现定义的方式。如果发生这种情况,那么整数数组将不会与内存布局对齐 struct (除非填充仅在结尾处 struct, 也许)。变量的顺序 struct 在内存中应该跨实现保持一致(在该变量中) a 接下来是 b 其次是 c 在内存中),但是,再次,之间的字节填充仍然可以存在(例如, a 接下来是 b 在内存中,但之间有填充 a 和 b 这样他们就不会在记忆中彼此相继)。

对于像gcc这样的编译器, 有办法修改它如何处理填充。这可以帮助保证 struct 将与您的整数数组在内存中对齐,但这可能会导致下游的其他内存问题。

换句话说,规范是否要求myColor.colorChannels [0]的内存地址与myColor.red的地址相同?

如果之间没有填充 redgreen,和 blue 的 struct,那么索引 colorChannels 将与内存中的每个变量匹配。


8
2018-02-22 23:12



从语言律师的角度来看,这是正确的;考虑到数组不能有填充,但必须仍然正确对齐,这将是 非常令人惊讶 为结构选择不同的包装。另一方面,这是一个可能在C中起作用但在C ++中被彻底禁止的技巧,所以不是最好的想法。 - Bo Persson


C标准对此没有任何保证,但它可能对任何存在的实际系统都是正确的。

我建议在代码中添加一个编译时检查,这样如果有一些系统添加了填充,你会得到一个编译错误,你可以在那时处理:

_Static_assert( sizeof(Color) == 3 * sizeof(int),  "We're on a weird system boys." );

NB。 _Static_assert 在C11中添加,在此之前你可以使用一些黑客 如此处所述


5
2018-02-22 23:39



有趣。我不知道_Static_assert。谢谢你的建议! - EKW
我赞成一个现实世界的答案。 - alain