8

可能重复:
将位字段转换为 int

我正在开发一个应用程序,其中一部分处理包含许多 1 位标志的 16 位字。我正在使用类似于下图的结构处理数据:

struct mystruct
{
   uint16_t Reserved1   :3;
   uint16_t WordErr     :1;
   uint16_t SyncErr     :1;
   uint16_t WordCntErr  :1;
   uint16_t Reserved2   :10;
};

即,该结构包含一个 16 位变量,该变量作为多个较小的(在某些情况下为 1 位标志)片段进行处理。

我的问题是,有没有一种简单的方法可以将整个 16 位字作为一个值来处理,例如,将其输出到控制台、文件或将其添加到另一个数据结构中?除了移动各个结构元素并将它们添加到临时 uint16_t 变量之外,我不知道有任何方法可以做到这一点。似乎有一种更简单的方法可以提取整个单词,但我找不到任何关于编译器如何处理这样的结构的信息。

编辑:我想这可能很明显,但简而言之,我想要做的是能够单独访问 1 位标志,并将结构用作 uint16_t 类型的单个变量(即 unsigned short,16 位)。

4

4 回答 4

11

这里的标准方法是使用匿名结构/联合,如下所示:

union mystruct
{
   struct
   {
      uint16_t Reserved1   :3;
      uint16_t WordErr     :1;
      uint16_t SyncErr     :1;
      uint16_t WordCntErr  :1;
      uint16_t Reserved2   :10;
   };

   uint16_t word_field;
};

或者,如果 union 不能作为顶级对象,

struct mystruct
{
   union
   {
       struct
       {
          uint16_t Reserved1   :3;
          uint16_t WordErr     :1;
          uint16_t SyncErr     :1;
          uint16_t WordCntErr  :1;
          uint16_t Reserved2   :10;
       };

       uint16_t word_field;
   };
};

此定义允许直接访问内部字段,例如:

mystruct s1;
s1.WordCntErr = 1;

严格来说,编译器并没有保证联合体的不同成员如何相互重叠。它可以使用不同的对齐方式甚至移位。这里有很多人会很容易地指出这一点。然而,从实际的角度来看,如果联合的所有字段具有相同的大小,您可以安全地假设它们占用同一块内存。例如,代码

s1.word_field = 0;

将所有位字段清零。大量代码正在使用它。无法想象这将永远停止工作。

于 2012-09-18T18:34:26.620 回答
4

简短的回答是你做不到。更长的答案是你可以做到,但细节取决于你的编译器。这种特殊的位域布局看起来很可疑,就像它应该映射到硬件寄存器一样,在这种情况下,您已经有了编译器依赖项:位域如何排列的细节是实现定义的。因此,当您向自己保证编译器按您期望的方式布置它们时,您还可以通过联合检查它是否支持类型双关语。尽管在 C 和 C++ 中写入联合的一个字段并从另一个字段读取正式会产生未定义的行为,但大多数(全部?)编译器在像这样的简单情况下都支持它。

于 2012-09-18T18:48:52.007 回答
2

来自联合技术的未定义行为的替代方法是,您可以复制数据:

mystruct m;
m.Reserved1 = 0;
m.WordErr = 1;
m.SyncErr = 0;
m.WordCntErr = 0;
m.Reserved2 = 0;

uint16_t value = 0;
memcpy(&value, &m, sizeof(value));

[代码]

Of course, the output is platform-specific / endian-sensitive, so if you plan on writing it out so you can read it in again then take that into account.

于 2012-09-18T22:10:18.360 回答
1

这就是工会的用途。我几乎不需要使用一个,所以我的语法可能很生疏,但它看起来像这样:

union myunion
{
    struct mystruct
    {
       uint16_t Reserved1   :3;
       uint16_t WordErr     :1;
       uint16_t SyncErr     :1;
       uint16_t WordCntErr  :1;
       uint16_t Reserved2   :10;
    };
    uint16_t word;
};

当然,这会在您访问它时添加输入,因此如果您只是偶尔需要它,您可能只想尝试一个类型转换。

于 2012-09-18T18:32:20.183 回答