这个工会合法吗?
Is this union legal?
我正在修改和清理前同事的代码。我创建了一个联合体,通过他设计的UART传输协议来简化消息的“解析”和“构造”。
示例消息:!cx:000:2047
第一个字符表示参数(由第二个和第三个字符定义,在本例中为cx
)是否应该设置(!
)或返回(?
)到消息的发送者。如果需要,消息的地址部分 (000
) 定义数组中的索引,指定值(在本例中为 2047
)应写入该数组。命令、地址和值使用 :
字符分隔。
我本可以使用 strtok()
使用分隔符来分隔消息,但接收缓冲区的大小固定为 12 个字符,发件人功能确保消息的格式正确,我找到了这个方法是最容易阅读和理解的。
但是现在,我看到了 this 话题,我不确定下面的联合是否合法:
typedef union UartMessage
{
char msg[12];
struct
{
char dir; //[0]: set/get
char param[2]; //[1-2]: parameter to set/get
char separator1; //[3]: ':' separator
char addr[3]; //[4-6]: memory array index (used for current and speed arrays)
char separator2; //[7]: ':' separator
char value[4]; //[8-11]: value to set
};
} uartMsg_t;
自 C11 起添加匿名结构是合法的。
但是,如果嵌入式结构中没有引入额外的填充,我建议添加一个断言。
_Static_assert(offsetof(uartMsg_t, value[4]) == 12, "extra padding");
C 和 C++ 在“联合类型双关”方面是不同的。在 C 中,我们可能写入一个联合成员并从另一个成员读取,之后二进制值被转换为新类型。假设对齐没有问题,并且可以根据值表示新类型,这就是 C 中的 well-defined1)。在 C++ 中执行相同操作会调用未定义的行为.
至于你的union
在用C编译时能不能用,这取决于对齐方式。仅由 char
成员(或它们的数组)组成的 struct/union 不应该有任何填充,尽管理论上编译器可以自由插入它。最佳做法包括进行 compile-time 检查以确保这不是问题:
_Static_assert(sizeof(uartMsg_t) == sizeof((uartMsg_t){0}.msg),
"Padding detected!");
在其他情况下,当您确实有填充时,您可能必须使用 non-standard #pragma pack
或类似的方式明确禁用它。使用结构表示数据协议的最大可移植程序需要调用显式 seralization/deserialization 例程(可移植性优于性能)。
1) 来源:C17 6.5.2.3+注97), C17 6.2.6
我正在修改和清理前同事的代码。我创建了一个联合体,通过他设计的UART传输协议来简化消息的“解析”和“构造”。
示例消息:!cx:000:2047
第一个字符表示参数(由第二个和第三个字符定义,在本例中为cx
)是否应该设置(!
)或返回(?
)到消息的发送者。如果需要,消息的地址部分 (000
) 定义数组中的索引,指定值(在本例中为 2047
)应写入该数组。命令、地址和值使用 :
字符分隔。
我本可以使用 strtok()
使用分隔符来分隔消息,但接收缓冲区的大小固定为 12 个字符,发件人功能确保消息的格式正确,我找到了这个方法是最容易阅读和理解的。
但是现在,我看到了 this 话题,我不确定下面的联合是否合法:
typedef union UartMessage
{
char msg[12];
struct
{
char dir; //[0]: set/get
char param[2]; //[1-2]: parameter to set/get
char separator1; //[3]: ':' separator
char addr[3]; //[4-6]: memory array index (used for current and speed arrays)
char separator2; //[7]: ':' separator
char value[4]; //[8-11]: value to set
};
} uartMsg_t;
自 C11 起添加匿名结构是合法的。
但是,如果嵌入式结构中没有引入额外的填充,我建议添加一个断言。
_Static_assert(offsetof(uartMsg_t, value[4]) == 12, "extra padding");
C 和 C++ 在“联合类型双关”方面是不同的。在 C 中,我们可能写入一个联合成员并从另一个成员读取,之后二进制值被转换为新类型。假设对齐没有问题,并且可以根据值表示新类型,这就是 C 中的 well-defined1)。在 C++ 中执行相同操作会调用未定义的行为.
至于你的union
在用C编译时能不能用,这取决于对齐方式。仅由 char
成员(或它们的数组)组成的 struct/union 不应该有任何填充,尽管理论上编译器可以自由插入它。最佳做法包括进行 compile-time 检查以确保这不是问题:
_Static_assert(sizeof(uartMsg_t) == sizeof((uartMsg_t){0}.msg),
"Padding detected!");
在其他情况下,当您确实有填充时,您可能必须使用 non-standard #pragma pack
或类似的方式明确禁用它。使用结构表示数据协议的最大可移植程序需要调用显式 seralization/deserialization 例程(可移植性优于性能)。
1) 来源:C17 6.5.2.3+注97), C17 6.2.6