【问题标题】:How to access bits in mapped memory?如何访问映射内存中的位?
【发布时间】: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


【解决方案1】:

位域依赖于编译器的程度被夸大了。当您以奇怪的方式使用它们时,它们通常会变得很奇怪。

在您的示例中,所有字段都是无符号的并且具有相同的存储类型,它们都没有跨越存储边界,并且没有未使用的位。存在排序位的问题(如您所见),但近年来每个人都采用相同的排序。据我所知,当前的编译器不会产生任何与您获得的 IO 数据不兼容的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 2013-06-19
    • 1970-01-01
    • 2019-09-25
    • 2020-11-09
    相关资源
    最近更新 更多