【发布时间】:2017-02-21 09:17:52
【问题描述】:
想象一下!我从处理一些家庭自动化的 IO 接口接收 8 个字节作为 uint8_t data[8]。这八个字节需要解释为休闲:
如何访问正确的位? 我现在想到了两种解决方案:
解决方案 1:使用一些位域定义结构:
struct data_s {
uint8_t LightSwitch0:1;
uint8_t LightSwitch1:1;
uint8_t LightSwitch2:1;
uint8_t LightSwitch3:1;
uint8_t Brightness:4;
uint8_t Dimmer0;
uint8_t Dimmer1;
uint8_t Dimmer2;
uint8_t Dimmer3;
uint8_t Dimmer4;
uint8_t DoorSwitch:1;
uint8_t Spare:7;
}
接下来我可以根据这个例子简单地做一个转换并访问结构成员:
data_s *foo = data;
foo->LightSwtich1 = FALSE;
foo->Brightness = 7;
//...
解决方案 2:使用位掩码:
data[0] |= 0x02;
data[0] = (data[0] & 0x0F) | (( 7 << 4 ) & 0xF0)
//...
好吧,我知道位域依赖于编译器并且不是那么可移植的。这就是为什么通常首选解决方案 2 的原因。然而,解决方案 2 看起来更复杂,更难阅读。此外,如果映射的内存超过 8 个字节,这将是很多工作。
我真的需要解决方案2吗?或者我可以使用#pragma reverse_bitfields on 指令来提高解决方案1 的可移植性?如果代码是为一个使用交叉编译器的目标构建的,我可以使用解决方案 1 吗?
所以我的问题是:我应该如何访问正确的位?
【问题讨论】:
-
位域是可移植的并且是 C 标准的一部分。这是代码可读性和习惯的问题:就我而言,我更喜欢位掩码,但很多人更喜欢位文件。
-
MISRA曾经禁止使用bitfields,不知道现在还没有。
-
您可以使用 #define 宏使解决方案 2 看起来更具可读性。你最终会在一个地方看到难看的东西,这有一个额外的好处,那就是减少未来进行更改所需的工作量。
-
扩展一点@ChrisTurner 的建议,您可以将其抽象为一个函数,并使用枚举作为参数,其中每个枚举值都是具有好听名称的掩码。
-
便携性不是主要焦点。但这仍然很重要,因为该软件在嵌入式设备上正常运行,并且部分代码可以由普通的基于英特尔的处理器运行。
标签: c gcc bit-manipulation cross-compiling bit-fields