我应该使用位字段来映射传入的串行数据吗?

问题描述 投票:2回答:1

我们通过串行(蓝牙)传输数据,这些数据映射到特定结构。结构的某些部分是子字节大小,因此“明显”的解决方案是将输入数据映射到位字段。我无法解决的是机器或编译器的位端特性是否会影响它(难以测试),以及我是否应该完全放弃位字段。

例如,我们有一个1.5字节的数据,所以我们使用了结构:

{
    uint8_t data1; // lsb
    uint8_t data2:4; // msb
    uint8_t reserved:4;
} Data;

保留位始终为1

因此,例如,如果传入数据是0xD2,0xF4,则值为0x04D2或1234。

我们使用的结构总是在我们测试的系统上工作,但我们需要它尽可能地便携。

我的问题是:

  • 无论字节顺序如何,data1总是表示正确的值(我假设是,并且硬件/软件接口应始终正确处理单个整个字节 - 如果发送0xD2,则应接收0xD2)?
  • data2reserved可能是错误的方式,data2代表高4位而不是低4位?

如是:

  • 位字节式(通常)是否依赖于字节字节序,或者它们是否完全不同?
  • bit-endianness是由硬件还是编译器决定的?看来英特尔的所有Linux系统都是一样的 - 对于ARM来说也是如此吗? (如果我们可以说我们可以支持所有Intel和ARM linux版本,我们应该没问题)
  • 是否有一种简单的方法可以在编译器中确定它的方式,并在需要时保留位字段条目?

虽然比特字段是代码方面最好的方式来映射传入的数据,但我想我只是想知道放弃它们是否更安全,并使用类似的东西:

struct {
    uint8_t data1; // lsb (0xFF)
    uint8_t data2; // msb (0x0F) & reserved (0xF0)
} Data;

Data d;

int value = (d.data2 & 0x0F) << 16 + d.data1

我们之所以这么做的原因是因为许多数据字段小于1个字节,而不是大于1个 - 这意味着通常使用位字段我们不需要进行任何屏蔽和移位,所以后处理更简单。

c endianness bit-fields
1个回答
3
投票

我应该使用位字段来映射传入的串行数据吗?

不会。比特字段有很多实现指定的行为,这使得使用它们成为一场噩梦。

无论字节顺序如何,data1总是表示正确的值。

是的,但这是因为uint8_t是可能的最小可寻址单位:一个字节。对于较大的数据类型,您需要处理字节字节序。

数据2和保留可能是错误的方式,data2代表高4位而不是低4位?

是。它们也可能在不同的字节上。此外,编译器不必支持uint8_t用于位域,即使它支持该类型也是如此。

位字节式(通常)是否依赖于字节字节序,或者它们是否完全不同?

最低有效位始终位于最低有效字节中,但不可能在C中确定该位在字节中的位置。

位移运算符可以可靠地抽象出足够好的顺序:对于数据类型uint8_t(1u << 0)始终是最不重要的,(1u << 7)是所有编译器和所有体系结构中最重要的位。

另一方面,位字段的定义很差,您无法按定义字段的顺序确定位的顺序。

bit-endianness是由硬件还是编译器决定的?

编译器指示数据类型如何映射到实际位,但硬件会严重影响它。对于位字段,相同硬件的两个不同编译器可以按不同顺序放置字段。

是否有一种简单的方法可以在编译器中确定它的方式,并在需要时保留位字段条目?

并不是的。这取决于你的编译器如何做,如果可能的话。

虽然比特字段是代码方面最好的方式来映射传入的数据,但我想我只是想知道放弃它们是否更安全,并使用类似的东西:

绝对放弃位字段,但我也建议为此目的完全放弃结构,因为:

  • 您需要使用编译器扩展或手动工作来处理字节顺序。
  • 您需要使用编译器扩展来禁用填充以避免由于对齐限制而产生的间隙。这会影响某些系统上的成员访问性能。
  • 您不能拥有可变宽度或可选字段。
  • 如果您不了解这些问题,则很容易出现严格的别名冲突。如果为数据框定义字节数组并将其转换为指向结构的指针,然后取消引用该数组,则在许多情况下会出现问题。

相反,我建议手动完成。定义字节数组,然后通过在必要时使用位移和屏蔽将它们分开来手动将每个字段写入其中。您可以为基本数据类型编写简单的可重用转换函数。

© www.soinside.com 2019 - 2024. All rights reserved.