首页 > 解决方案 > 是否有任何可移植的方法来确保在不使用 c++11 填充字节的情况下定义结构?

问题描述

让我们考虑以下任务:我的 C++ 模块作为嵌入式系统的一部分接收 8 个字节的数据,例如:uint8_t data[8]。第一个字节的值决定了其余部分的布局(20-30 不同)。为了有效地获取数据,我将为每个布局创建不同的结构并将每个结构放入一个联合中,并通过如下指针直接从我的输入地址读取数据:

struct Interpretation_1 {
    uint8_t multiplexer;
    uint8_t timestamp;
    uint32_t position;
    uint16_t speed;
};
// and a lot of other struct like this (with bitfields, etc..., layout is not defined by me :( )

union DataInterpreter {
    Interpretation_1 movement;
    //Interpretation_2 temperatures;
    //etc...
};

...
uint8_t exampleData[8] {1u, 10u, 20u,0u,0u,0u, 5u,0u};
DataInterpreter* interpreter = reinterpret_cast<DataInterpreter*>(&exampleData);
std::cout << "position: " << +interpreter->movement.position << "\n";

我遇到的问题是,编译器可以在解释结构中插入填充字节,这扼杀了我的想法。我知道我可以使用

但是有没有可移植的方法来实现这一点?我知道 c++11 有例如 alignas 用于对齐控制,但我可以用它来做这个吗?我必须使用 c++11,但如果有更高版本的 c++ 更好的解决方案,我会很感兴趣。

标签: c++c++11structmemory-alignmentmemory-layout

解决方案


但是有没有可移植的方法来实现这一点?

不,没有(标准)方法可以“制作”一种在 C++ 中具有填充而不具有填充的类型。所有对象都至少按照它们的类型要求对齐,如果对齐与之前的子对象不匹配,那么就会有填充,这是不可避免的。

此外,还有另一个问题:您正在通过重新解释的指针访问,该指针不指向兼容类型的对象。程序的行为是未定义的。

我们可以得出结论,类通常不适用于表示任意二进制数据。打包结构是非标准的,并且它们在具有不同整数表示(字节字节序)的不同系统之间也不兼容。


有一种方法可以检查一个类型是否包含填充:将子对象的大小与完整对象的大小进行比较,并递归地对每个成员执行此操作。如果大小不匹配,则有填充。然而,这非常棘手,因为 C++ 的反射能力极少,因此您需要使用硬编码或元编程。

鉴于这样的检查,您可以在假设不成立的系统上使编译失败。

另一个方便的工具是std::has_unique_object_representations(自 C++17 起),对于所有具有填充的类型,它始终为 false。但请注意,例如,对于包含浮点数的类型,它也是错误的。只有返回 true 的类型才能有意义地比较与 的相等性std::memcmp


推荐阅读