联盟数据结构对齐

Myf*_*wik 5 c standards struct unions

我正在使用一些(我认为是)具有联合的坏代码:

union my_msg_union
{
  struct message5;
  char buffer[256]
} message;
Run Code Online (Sandbox Code Playgroud)

缓冲区中有来自通信的256个字节.结构类似于:

struct message5 {
 uint8 id;
 uint16 size;
 uint32 data;
 uint8 num_ids;
 uint16 ids[4];
} message5d
Run Code Online (Sandbox Code Playgroud)

在大量架构(8位AVR,16位菲利普斯,32位臂,32位x86和amd64)上编译相同的代码.

我认为的问题是使用了union:代码只是一串连续收到的字节进入缓冲区,然后通过结构读出值,而不考虑结构的对齐/填充.

果然,快速查看不同系统上的sizeof(message5d)给出了不同的结果.

然而令我感到惊讶的是,每当与char []的联合存在时,所有类型的所有结构的所有实例在所有系统上都会丢弃它们的填充/对齐,并确保是连续的字节.

这是C标准还是编译器作者为"帮助"提供的东西?

Jon*_*ler 3

此代码演示了与您描述的行为相反的行为:

\n\n
#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n\nstruct message5\n{\n    uint8_t id;\n    uint16_t size;\n    uint32_t data;\n    uint8_t num_ids;\n    uint16_t ids[4];\n};\n\n#if !defined(NO_UNION)\nunion my_msg_union\n{\n    struct message5 msg;\n    char buffer[256];\n};\n#endif /* NO_UNION */\n\nstruct data\n{\n    char const *name;\n    size_t offset;\n};\n\nint main(void)\n{\n    struct data offsets[] =\n    {\n        { "message5.id", offsetof(struct message5, id) },\n        { "message5.size", offsetof(struct message5, size) },\n        { "message5.data", offsetof(struct message5, data) },\n        { "message5.num_ids", offsetof(struct message5, num_ids) },\n        { "message5.ids", offsetof(struct message5, ids) },\n#if !defined(NO_UNION)\n        { "my_msg_union.msg.id", offsetof(union my_msg_union, msg.id) },\n        { "my_msg_union.msg.size", offsetof(union my_msg_union, msg.size) },\n        { "my_msg_union.msg.data", offsetof(union my_msg_union, msg.data) },\n        { "my_msg_union.msg.num_ids", offsetof(union my_msg_union, msg.num_ids) },\n        { "my_msg_union.msg.ids", offsetof(union my_msg_union, msg.ids) },\n#endif /* NO_UNION */\n    };\n    enum { NUM_OFFSETS = sizeof(offsets) / sizeof(offsets[0]) };\n\n    for (size_t i = 0; i < NUM_OFFSETS; i++)\n        printf("%-25s  %3zu\\n", offsets[i].name, offsets[i].offset);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

示例输出(Mac OS X 10.9 Mavericks 上的 GCC 4.8.2,64 位编译):

\n\n
message5.id                  0\nmessage5.size                2\nmessage5.data                4\nmessage5.num_ids             8\nmessage5.ids                10\nmy_msg_union.msg.id          0\nmy_msg_union.msg.size        2\nmy_msg_union.msg.data        4\nmy_msg_union.msg.num_ids     8\nmy_msg_union.msg.ids        10\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如 C 标准所要求的,联合内的偏移量与结构内的偏移量相同。

\n\n

如果您确实可以重现异常答案,则必须根据上面的代码给出完整的编译反例,并指定正在编译的编译器和平台以获得异常答案 \xe2\x80\x94 。

\n\n

我注意到我必须将uint8etc 更改为uint8_t,但我认为这没有任何区别。如果是这样,您需要指定从哪个标头获取名称uint8

\n\n
\n\n

代码更新为无论有或没有union. 编译时的输出-DNO_UNION

\n\n
message5.id                  0\nmessage5.size                2\nmessage5.data                4\nmessage5.num_ids             8\nmessage5.ids                10\n
Run Code Online (Sandbox Code Playgroud)\n

  • @slebetman:我不认为我同意,但是安排在没有联合的情况下编译代码是微不足道的,并且结构的输出与使用联合的输出相同 - 当然。 (2认同)