我需要使用打包结构来解析传入的数据。我还有一个 std::Optional 值,我想将其分配给结构成员之一的值。然而,它失败了。我想我理解这个问题,基本上引用与内存宽度不对齐的变量可能是一件坏事。
代码示例使用 clang 14 进行编译,但不能使用 gcc 12 进行编译。这是一个错误还是一个“功能”?
#include <cstdint>
#include <optional>
struct Data {
uint8_t a{};
uint32_t b{};
} __attribute__((packed));
int main() {
Data t;
std::optional<uint32_t> val{};
val = t.b; // <<< this failes
val = (decltype(t.b)) t.b;
}
clang:https://godbolt.org/z/eWfaqb3a3
海湾合作委员会:https://godbolt.org/z/or1W5MbdG
我知道打包结构的“一般问题”。由于我的目标是具有 x86-64 的嵌入式设备,并且正在解析的数据来自工业标准总线,因此我相信我是安全的。
ARM CPU 有一个位表示未对齐访问是否会导致处理器陷阱,或者由 CPU 使用较慢的访问方法透明地处理。 Clang 默认 CPU 允许未对齐访问,而 GCC 默认 CPU 捕获未对齐访问。
因此对于 Clang 来说,为未对齐的
int&
创建一个
t.b
是完全可以的,因为 CPU 会透明地处理可能导致的未对齐访问。另一方面,对于 GCC,从 int&
创建
t.b
会带来代码访问它并导致陷阱的风险。采用 int&
的函数的合约规定 int
必须对齐。所以编译器失败,因为合同无法得到满足。但是,如果您编写 (decltype(t.b)) t.b;
,您将创建要使用的
t.b
的副本,从而避免未对齐的问题,因为编译器知道如何复制未对齐的 uint32_t
。您可以指定编译器标志来更改有关未对齐访问的默认假设。允许 GCC 应该使代码编译,但假设您的 ARM CPU 将配置为允许所述未对齐访问。