我在 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__));
我的问题是:
- 假设我对 MCU 和客户端应用程序上的
TelemetryPacket
结构使用完全相同的定义,上述代码是否可以跨多个平台移植? (我对 x86 和 x86_64 感兴趣,需要它在 Windows、Linux 和 OS X 上运行。) - 其他编译器是否支持具有相同内存布局的打包结构?用什么语法?
编辑:
- 是的,我知道打包结构是非标准的,但它们似乎很有用,可以考虑使用它们。
- 我对 C 和 C++ 都感兴趣,尽管我认为 GCC 不会以不同的方式处理它们。
- 这些结构不是继承的,也不会继承任何东西。
- 这些结构仅包含固定大小的整数字段,以及其他类似的打包结构。 (我以前被花车烧过……)
原文由 Venemo 发布,翻译遵循 CC BY-SA 4.0 许可协议
你永远不应该在编译域中使用结构体,而不是内存(硬件寄存器,从文件中分离读取的项目或在处理器或同一处理器不同软件(应用程序和内核驱动程序之间)之间传递数据)。你是在自找麻烦,因为编译器有一定的自由意志来选择对齐,然后用户可以通过使用修饰符使情况变得更糟。
不,没有理由假设您可以跨平台安全地执行此操作,即使您使用相同的 gcc 编译器版本,例如针对不同的目标(编译器的不同构建以及目标差异)。
为了降低失败的几率,首先从最大的项目开始(64 位,然后是 32 位,16 位,最后是任何 8 位项目)理想情况下,至少对齐 32 位,也许是 64,这是希望 arm 和 x86 做的,但这总是可以改变为以及从源代码构建编译器的任何人都可以修改默认值。
现在,如果这是一项工作安全问题,请确保继续,您可以对此代码进行定期维护,可能需要为每个目标定义每个结构(因此,ARM 结构定义的源代码副本和另一个对于 x86,或者如果不是立即需要,最终将需要它)。然后每一个或每几个产品发布,你都会被叫来处理代码……漂亮的小维护定时炸弹会爆炸……
如果您想在相同或不同架构的编译域或处理器之间安全地进行通信,请使用某种大小的数组、字节流、半字流或字流。显着降低故障和维护的风险。不要使用结构来分离那些只会恢复风险和失败的项目。
人们似乎认为这没问题的原因是因为您对相同的目标或系列(或从其他编译器选择派生的编译器)使用相同的编译器或系列,因为您了解语言的规则以及实现定义的区域在哪里最终会遇到不同,有时你的职业生涯需要几十年,有时需要几周……这是“在我的机器上工作”的问题……