在struct c ++/arduino中打包位

Ken*_*ent 23 c++ struct arduino

我有一个结构:

typedef struct {
  uint8_t month;  // 1..12 [4 bits]
  uint8_t date;   // 1..31 [5 bits]
  uint8_t hour;   // 00..23 [5 bits]
  uint8_t minute; // 00..59 [6 bits]
  uint8_t second; // 00..59 [6 bits]
} TimeStamp;
Run Code Online (Sandbox Code Playgroud)

但我想打包它所以它只消耗4个字节而不是5个字节.

有没有办法改变位以创建更严格的结构?

它可能看起来不多,但是它会进入EEPROM,因此在4Kb页面中保存1个字节是额外的512个字节(我可以使用剩下的6个额外的其他东西).

Ian*_*ing 29

您正在寻找的是位域.

它们看起来像这样:

typedef struct {
  uint32_t month  : 4;   // 1..12 [4 bits]
  uint32_t date   : 5;   // 1..31 [5 bits]
  uint32_t hour   : 5;   // 00..23 [5 bits]
  uint32_t minute : 6;   // 00..59 [6 bits]
  uint32_t second : 6;   // 00..59 [6 bits]
} TimeStamp;
Run Code Online (Sandbox Code Playgroud)

根据您的编译器,为了在没有填充的情况下适合4个字节,uint32_t在这种情况下,成员的大小必须是4个字节(即).否则结构成员将填充到每个字节边界不溢出,如果使用,则产生5字节的结构uint8_t.将此作为一般规则应有助于防止编译器差异.

这是一个MSDN链接,深入到位域:

C++位域

  • @Cubic也是"singed int".处理器冷却不够......? (6认同)
  • 所有C编译器都只支持`unsigned int`,`singed int`和`_Bool`的位域(也允许使用`int`,但在位域的上下文中,'int`可以是有符号或无符号的,取决于实现,所以真正使用它没有意义).GCC支持其他类型作为扩展.`uint32_t`可能是`unsigned int`的typedef,所以这可能是兼容的(更好的是显式并使用`unsigned int`).此外,未指定对齐,因此在此示例中,没有最后的位字段,可能存在12位填充. (5认同)
  • 由于您链接到MSDN,值得一提的是,不同的编译器也可能会应用其他限制:我的经验是VS将在声明类型的更改上添加新的存储单元,而GCC(Arduino使用)将继续打包只要不溢出,不同类型的存储单元就可以存储在同一存储单元中.当然,始终使用相同类型仍然更好. (4认同)

Cub*_*bic 14

一般来说,Bitfields是一种"正确"的方式,但为什么不从年初开始存储秒数呢?4个字节足以舒适地存储这些; 实际上,4个字节足以存储1970年到2038年之间的秒数.只要您知道当前年份(您可以将其与其他信息一起存储),从中获取其他信息就是一个简单的练习.作为您感兴趣的覆盖时间不到70年的时间范围(即便如此,您可以将时间戳分组为68年范围并为每个范围存储偏移量).

  • 这样存储秒的一个问题是计算实际日期是很昂贵的,尤其是.在嵌入式系统中,当你没有硬件划分甚至乘法时 (3认同)

Ser*_*sen 12

另一种解决方案是将值存储在一个32位变量中,并使用位移检索各个项目.

uint32_t timestamp = xxxx;

uint8_t month = timestamp & 0x0F;
uint8_t date = (timestamp & 0x1F0) >> 4;
uint8_t hour = (timestamp & 0x3E00) >> 9;
uint8_t minute = (timestamp & 0xFC000) >> 14;
uint8_t second = (timestamp & 0x3F00000) >> 20;
Run Code Online (Sandbox Code Playgroud)

  • 这是程序员驱动的位域.:) (3认同)