Ven*_*emo 40 c c++ gcc struct packed
我在Cortex-M4微控制器上有一些代码,并希望使用二进制协议与PC通信.目前,我正在使用具有GCC特定packed属性的压缩结构.
这是一个粗略的轮廓:
struct Sensor1Telemetry {
int16_t temperature;
uint32_t timestamp;
uint16_t voltageMv;
// etc...
} __attribute__((__packed__));
struct TelemetryPacket {
Sensor1Telemetry tele1;
Sensor2Telemetry tele2;
// etc...
} __attribute__((__packed__));
Run Code Online (Sandbox Code Playgroud)
我的问题是:
TelemetryPacket对MCU和客户端应用程序上的结构使用完全相同的定义,上述代码是否可以跨多个平台移植?(我对x86和x86_64很感兴趣,需要它在Windows,Linux和OS X上运行.)编辑:
gez*_*eza 25
考虑到上述平台,是的,打包结构完全可以使用.x86和x86_64始终支持未对齐访问,与普遍看法相反,这些平台上的未对齐访问(几乎)与对齐访问的速度相差很长时间(没有这样的事情,未对齐的访问速度要慢得多).唯一的缺点是访问可能不是原子的,但在这种情况下我认为不重要.并且编译器之间存在协议,打包的结构将使用相同的布局.
GCC/clang支持使用您提到的语法的打包结构.MSVC有#pragma pack,可以像这样使用:
#pragma pack(push, 1)
struct Sensor1Telemetry {
int16_t temperature;
uint32_t timestamp;
uint16_t voltageMv;
// etc...
};
#pragma pack(pop)
Run Code Online (Sandbox Code Playgroud)
可能会出现两个问题:
movaps或ldrd),那么你可能会使用该指针崩溃(gcc没有不要警告你这件事,但是铿锵有力.以下是海湾合作委员会的文件:
packed属性指定变量或结构字段应具有最小可能的对齐 - 变量的一个字节
所以GCC 保证不会使用填充.
MSVC:
打包类是将其成员直接放在内存中
所以MSVC 保证不会使用填充.
我发现的唯一"危险"区域是位域的使用.然后,GCC和MSVC之间的布局可能不同.但是,GCC中有一个选项,它使它们兼容:-mms-bitfields
提示:即使如果此解决方案现在可以正常工作,并且它不太可能停止工作,我建议您保持代码对此解决方案的依赖性较低.
注意:我在这个答案中只考虑了GCC,clang和MSVC.可能有编译器,但这些事情并非如此.
alk*_*alk 12
如果
然后是的," 打包结构 "是便携式的.
对于我的口味太多"如果",不要这样做.麻烦不值得出现.
您可以这样做,或使用更可靠的替代方案.
对于序列化狂热者中的核心,有CapnProto.这为您提供了一个本地结构来处理,并承诺确保当它通过网络传输并轻松处理时,它仍然有意义的另一端.称之为序列化几乎是不准确的; 它旨在尽可能地对结构的内存表示做一点.可能适合移植到M4
有Google协议缓冲区,这是二进制文件.更臃肿,但相当不错.随附的nanopb(更适合微控制器),但它没有完成整个GPB(我不认为它oneof).很多人虽然成功地使用它.
一些C asn1运行时足够小,可用于微控制器.我知道这个适合M0.
你永远不应该在编译域中使用结构,内存(硬件寄存器,挑选从文件读取的项目或在处理器之间传递数据或在同一处理器上使用不同的软件(在应用程序和内核驱动程序之间)).你要求麻烦,因为编译器有一定的自由意志来选择对齐,然后用户可以通过使用修饰符使其变得更糟.
没有理由假设您可以跨平台安全地执行此操作,即使您使用相同的gcc编译器版本,例如针对不同的目标(编译器的不同版本以及目标差异).
为了降低失败的几率,首先要使用最大的项目(64位,然后是32位,16位,然后是最后的任何8位项目)理想情况下,最小值为32,最小可能为64,这可能是希望arm和x86做的,但总是可以改变为以及任何从源构建编译器的人都可以修改默认值.
现在,如果这是一个工作安全问题,请确保继续,您可以对此代码进行定期维护,可能需要为每个目标定义每个结构(因此,ARM的结构定义的源代码的一个副本和另一个对于x86,或者最终需要这个,如果没有立即).然后,您可以调用每个或每个产品版本来完成代码工作......很少的维护时间炸弹......
如果要在编译域或处理器之间安全地进行相同或不同的体系结构通信,请使用某种大小的数组,字节流,半字流或单词流.显着降低您的失败和维护风险.不要使用结构来挑选那些只是恢复风险和失败的物品.
人们似乎认为这是可以的,因为对相同的目标或系列(或从其他编译器选择派生的编译器)使用相同的编译器或系列,因为您了解语言的规则以及实现定义的区域在哪里最终会遇到差异,有时候你的职业生涯需要几十年的时间,有时需要数周才能完成......它的"在我的机器上工作"问题......