我必须定义一个通信协议(protocol),我想使用一个位域来存储一些逻辑值。
我在两个系统上工作:发送方:一个设备和一个作为接收方的 .Net 软件。
在固件方面,我通常定义一个位域结构,如:
struct __attribute__((__packed__)) BitsField
{
// Logic values
uint8_t vesselPresenceSw: 1;
uint8_t drawerPresenceSw: 1;
uint8_t pumpState: 1;
uint8_t waterValveState: 1;
uint8_t steamValveState: 1;
uint8_t motorDriverState: 1;
// Unused
uint8_t unused_0: 1;
uint8_t unused_1: 1;
};
我如何在支持字节反序列化的软件端定义相同的结构来构建结构本身?
最佳答案
恐怕没有直接的 C# 等同于 C 风格的位域结构。
C# 在一定程度上能够通过使用 FieldOffset
属性来近似 C 风格的 unions。这些明确的布局属性允许您指定精确的和可能重叠的字段偏移量。不幸的是,这甚至无法让您完成一半:必须以字节而不是位来指定偏移量,并且在读取或写入重叠字段时您不能强制执行特定宽度。
最接近原生支持位域的 C# 可能是基于标志的 enum
类型。如果您不需要超过 64 位,您可能会发现这已经足够了。首先根据适合所有标志的最小无符号类型声明一个enum
:
[Flags]
public enum BitFields : byte {
None = 0,
VesselPresenceSw = 1 << 0,
DrawerPresenceSw = 1 << 1,
PumpState = 1 << 2,
WaterValveState = 1 << 3,
SteamValveState = 1 << 4,
MotorDriverState = 1 << 5
}
命名的项目可以分配给它们适合底层类型(在本例中为 byte
)的任何值,因此如果您愿意,一个项目可以代表多个位。请注意,如果您想直接与 C 风格的位域互操作,您的第一个值应该从最高位开始,而不是最低位。
要使用您的标志,只需声明一个新类型的变量或字段并执行您需要的任何位运算:
BitFields bits = BitFields.None;
bits |= BitFields.VesselPresenceSw | BitFields.PumpState;
bits &= ~BitFields.VesselPresenceSw;
// etc.
从好的方面来说,使用 [Flags]
声明的枚举在调试器中显示或转换为字符串时可以很好地格式化。例如,如果您要打印表达式 BitFields.VesselPresenceSw | BitFields.PumpState
,您将得到文本 DrawerPresenceSw, PumpState
。
有一个警告:enum
的存储将接受适合底层类型的任何值。这样写是完全合法的:
BitFields badBits = (BitFields)0xFF;
这设置了 byte
大小的枚举的所有 8 位,但我们的命名值仅涵盖 6 位。根据您的要求,您可能希望声明一个仅包含“合法”标志的常量,您可以 &
反对。
如果您需要比这更丰富的内容,可以使用名为 BitArray 的框架级“位域”数据结构。 .但是,BitArray
是一种引用类型,它使用托管的 int[]
进行存储。如果您想要一个可用于互操作目的或任何类型的内存映射的 struct
,它不会帮助您。
关于c# - 如何在 C# 中静态模拟 C 位域?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46237984/