我需要使用打包结构来解析传入数据。我还有一个 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;
}
铿锵:https://godbolt.org/z/eWfaqb3a3
海湾合作委员会:https://godbolt.org/z/or1W5MbdG
我知道 general problems具有打包结构。由于我的目标是具有 x86-64 的嵌入式设备,并且正在解析的数据来自工业标准总线,因此我相信我是安全的。
最佳答案
这是 clang 和 gcc 如何假设 ARM cpu 配置的问题。
ARM cpu 有一个位,表示未对齐的访问是否会导致处理器陷阱,或者由 cpu 使用较慢的访问方法透明地处理。 Clang 默认 CPU 允许未对齐访问,而 gcc 默认 CPU 捕获未对齐访问。
因此,对于 clang 来说,为未对齐的 t.b
创建一个 int&
是完全可以的,因为 CPU 会透明地处理可能导致的未对齐访问。
另一方面,对于 gcc,从 t.b
创建 int&
存在代码访问它并导致陷阱的风险。采用 int&
的函数契约规定 int
必须对齐。因此编译器失败,因为契约(Contract)无法得到满足。
但是如果你写 (decltype(t.b)) t.b;
你会创建一个要使用的 t.b
的拷贝,这样就可以避免未对齐的问题,因为编译器知道如何复制未对齐的 uint32_t
。
您可以指定编译器标志来更改有关未对齐访问的默认假设。允许 gcc 应该会使代码编译,但假设您的 ARM cpu 将配置为允许所述未对齐访问。
关于c++ - gcc std::可选和打包结构成员的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72740619/