c - union 数据结构对齐

标签 c struct standards unions

我正在处理一些(我认为是)糟糕的代码,它们有一个像这样的 union :

union my_msg_union
{
  struct message5;
  char buffer[256]
} message;

缓冲区被来自通讯的 256 个字节填充。结构类似于:

struct message5 {
 uint8 id;
 uint16 size;
 uint32 data;
 uint8 num_ids;
 uint16 ids[4];
} message5d

相同的代码正在大量架构(8 位 AVR、16 位 phillips、32 位 arm、32 位 x86 和 amd64)上编译。

我认为问题是 union 的使用:代码只是将串行接收字节的 blob 放入缓冲区,然后通过结构读取值,而不考虑结构的对齐/填充。

果然,在不同的系统上快速查看 sizeof(message5d) 会得到不同的结果。

然而,令我惊讶的是,只要存在与 char [] 的 union ,所有系统上该类型的所有结构的所有实例都会放弃它们的填充/对齐,并确保是连续字节。

这是 C 标准还是编译器作者为了“帮助”而加入的东西?

最佳答案

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

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

struct message5
{
    uint8_t id;
    uint16_t size;
    uint32_t data;
    uint8_t num_ids;
    uint16_t ids[4];
};

#if !defined(NO_UNION)
union my_msg_union
{
    struct message5 msg;
    char buffer[256];
};
#endif /* NO_UNION */

struct data
{
    char const *name;
    size_t offset;
};

int main(void)
{
    struct data offsets[] =
    {
        { "message5.id", offsetof(struct message5, id) },
        { "message5.size", offsetof(struct message5, size) },
        { "message5.data", offsetof(struct message5, data) },
        { "message5.num_ids", offsetof(struct message5, num_ids) },
        { "message5.ids", offsetof(struct message5, ids) },
#if !defined(NO_UNION)
        { "my_msg_union.msg.id", offsetof(union my_msg_union, msg.id) },
        { "my_msg_union.msg.size", offsetof(union my_msg_union, msg.size) },
        { "my_msg_union.msg.data", offsetof(union my_msg_union, msg.data) },
        { "my_msg_union.msg.num_ids", offsetof(union my_msg_union, msg.num_ids) },
        { "my_msg_union.msg.ids", offsetof(union my_msg_union, msg.ids) },
#endif /* NO_UNION */
    };
    enum { NUM_OFFSETS = sizeof(offsets) / sizeof(offsets[0]) };

    for (size_t i = 0; i < NUM_OFFSETS; i++)
        printf("%-25s  %3zu\n", offsets[i].name, offsets[i].offset);
    return 0;
}

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

message5.id                  0
message5.size                2
message5.data                4
message5.num_ids             8
message5.ids                10
my_msg_union.msg.id          0
my_msg_union.msg.size        2
my_msg_union.msg.data        4
my_msg_union.msg.num_ids     8
my_msg_union.msg.ids        10

按照 C 标准的要求, union 内的偏移量与结构内的偏移量相同。

你必须根据上面的代码给出一个完整的编译反例,并指定你在哪个编译器和平台上编译才能得到你的异常答案——如果你确实可以重现异常答案的话。

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


代码已更新为可在有或没有union 的情况下进行编译。使用 -DNO_UNION 编译时的输出:

message5.id                  0
message5.size                2
message5.data                4
message5.num_ids             8
message5.ids                10

关于c - union 数据结构对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20463922/

相关文章:

c - void 类型到底是什么,它是如何工作的?

c - 如何解决C语言输入重复问题

c - MSP430 内存溢出

c++ - 在 C++17 中声明大小为 0 的数组是否合法?

c - 无法理解竞争性考试?

c - STM32F0 I2C HAL 保存结构到 I2C EEPROM

c++ - 在两台计算机之间发送带有意外错误字节的结构

c++ - int() 为 0 的定义在哪里?

c++ - 当::operator new 足够时,为什么需要::operator new[]?

c++ - 在声明和初始化指针后,什么时候给变量字面量加上星号前缀,什么时候不用?