【发布时间】:2019-12-18 13:00:02
【问题描述】:
我环顾了这个网站,试图弄清楚我对不同联合的强制转换是否违反了严格的别名或其他 UB。
我有数据包通过串行线路进入,我存储/获取它们:
union uart_data {
struct {
uint8_t start;
uint8_t addr;
uin16_t length;
uint8_t data[];
};
uint8_t bytes[BUFFER_SIZE];
};
void store_byte(uint8_t byte) {
uart_data->start = byte;
/* and so on with the other named fields. */
}
uint8_t * get_buffer() {
return uart_data->bytes;
}
我的理解是,至少对于 GCC 和 GNU 扩展来说,这是一种进行类型双关的有效方法。
但是,然后我想将返回值从 get_buffer() 转换为 uart 不需要了解详细信息的更具体类型的数据包。
union spec_pkt {
struct {
uint8_t start;
uint8_t addr;
uin16_t length;
uint8_t command;
uint8_t some_field;
uint16_t data_length;
uint8_t data[];
};
uint8_t bytes[BUFFER_SIZE];
};
void process(uint8_t *data) {
union specific_pkt *pkt = (union specific_pkt *)data;
}
我记得在某处读到这是有效的,因为我正在从联合中存在的类型进行强制转换,但我找不到源。
我这样做的理由是我可以拥有一个只需要了解最低级别细节的 uart 驱动程序。我在 MCU 上,所以我只能访问预先分配的数据缓冲区,这样我就不必在缓冲区之间使用memcpy,浪费空间。在我的应用程序代码中,我可以以更好的方式处理数据包:
uint8_t data[BUFFER_SIZE];
data[START_POS];
data[LEN_POS];
data[DATA_POS];
如果这违反了 SA 规则或者是 UB,我希望有一些替代方案来达到同样的效果。
我在支持非对齐访问的目标上使用 GCC,并且 GCC 允许通过联合进行类型双关。
【问题讨论】:
-
结构中的填充不完全取决于编译器吗?这意味着不同的构建可能会以不同的方式填充
bytes。 -
为什么不将您需要的所有可能的数据突变添加为联合中的不同结构?
-
如果您担心严格的别名,您可以随时使用
-fno-strict-aliasing进行编译 -
因为这是针对嵌入式系统的,所以是的,总是用
-fno-strict-aliasing -ffreestanding编译。 gcc 在设计时并没有真正考虑到嵌入式系统。 -
@JensGustedt:很多混淆源于这样一个事实,即该标准使用相同的术语来表示在某些情况下应该预期行为可预测的行为,但不是全部,基于实现特定的因素,如对于那些在任何情况下通常不应该表现出可预测性的人。出于某种原因,人们似乎认为标准使用术语“实施定义”来表示前者,但事实并非如此;实现定义保留用于所有实现应该总是可预测地处理的操作。
标签: c gcc casting strict-aliasing type-punning