【问题标题】:How consecutive bit-fields are converted to variable when casting强制转换时如何将连续位域转换为变量
【发布时间】:2019-04-04 12:33:10
【问题描述】:

我想在某些结构中使用标志,比如说:

struct {
    flag1:1;
    flag2:1;
    flag3:1;
    flag4:1;
}Flags;

标志 3 和 4 描述了我的目标项目的模式现在可以使用。我想将标志转换为数字以读取在 flag3 和 flag4 中设置的内容。 我要做的是:

uint8_t mode;
mode = ( ((uint8_t)Flags.flag4 << 1) | (uint8_t)Flags.flag3) );

让我吃惊的是它是这样工作的:

如果 flag4==1 且 flag3==1 'mode' 为 3

如果 flag4==1 和 flag3==0 'mode' 为 2 等等

我的问题是:为什么?我这样做是希望它有效,但我不知道为什么。

我在 Atollic 工作,负责 STM32 代码。

【问题讨论】:

  • 您是在要求我们解释您自己的代码吗?无意冒犯。这只是一个问题。
  • flag3在一个位上怎么能包含2?
  • @Broman 我知道我写了什么,只是不知道为什么会这样。
  • @MrPromethee 打错字了,应该是 0
  • 好的,我将添加一些解释: flag4=1 和 flag3=1 然后结构如下所示: 1100 如果我执行 flag4

标签: c casting structure data-conversion bit-fields


【解决方案1】:

因为 flag4 和 flag3 将被解释为二进制数中的数字。 flag4 将是最重要的。

Flags.flag4 &lt;&lt; 1 会将 flag4 向左移动,因此如果 flag4 为 1,则结果为 10。然后我们与 flag3 执行逻辑或。如果 flag3 为 1,这将产生 11,即二进制 3。

这里有一个小循环可以解释它。假设我们在一个数组中有 8 个标志,int flags[8]。现在我们要将这些压缩成uint8_t flagfield。这可以通过这个循环来完成:

flagfield=0;
for(int i=0; i<8; i++)
    flagfield |= (uint8_t)(flags[i] << i);

flagfield 现在将包含所有标志。如果您愿意,可以将其解释为常规整数。

【讨论】:

  • 所以当我将 flag4 转换为 uint8_t 时,它会被视为:0001?即使被其他领域包围?
【解决方案2】:

flag4 为 1 且flag3 为 1 时,flag4 &lt;&lt; 1 为 10(十进制为 2),因为您将 1 向左移动。使用|,您添加了flag3,它没有移位,所以您有11(十进制为3)。

flag3 为 0 时,它执行相同的操作,但结果为 10(十进制为 2),因为 flag3 为 0。

【讨论】:

    【解决方案3】:

    让我们剖析一下代码:

    mode = ( ((uint8_t)Flags.flag4

    第一:

    (uint8_t)Flags.flag4
    (uint8_t)Flags.flag3
    

    Flags.flag4 和 Flags.flag3 被强制转换为 uint8_t。这部分很重要。虽然 Flags.flag4 和 Flags.flag3 只有一位,但演员表将其变成了完整的 8 位。

    下一步:

    ((uint8_t)Flags.flag4 << 1)
    

    这会将 flag4 的值向左移动 1 位。与x * 2 相同。记住演员把它变成了一个完整的 8 位。所以班次不会溢出。 flag4 只能为 0 或 1,因此在移位后为 0 或 2。

    最后将这两个值组合在一起。根据 flag3 和 flag4 的值,结果为 0-3。

    请注意另一种编写方式,尽管特定于架构/ABI 是使用

    struct {
        uint8_t flag1:1;
        uint8_t flag2:1;
        union {
            struct {
                uint8_t flag3:1;
                uint8_t flag4:1;
            };
            uint8_t flag34:2;
        };
    } Flags;
    

    flag3 和 flag4 在内存中的顺序在架构/ABI 的调用约定中被指定,因此 flag34 不那么可移植。您可能需要重新设计标志以获得最佳结果(最短/最快的组装)。

    【讨论】:

      【解决方案4】:

      mode = ( ((uint8_t)Flags.flag4 &lt;&lt; 1) | (uint8_t)Flags.flag3) );

      何时:

      如果 flag4==1 和 flag3==0 'mode' 为 2

      解释:

      Flags.flag4 == 1
      1 << 1 == 2
      2 | 0 == 2
      

      如果 flag4==1 和 flag3==1 'mode' 为 3

      Flags.flag4 == 1
      1 << 1 == 2
      2 | 1 == 3
      

      你需要一本好的初学者 C 书

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-24
        • 1970-01-01
        • 1970-01-01
        • 2020-09-16
        • 2013-04-15
        • 2019-11-15
        • 1970-01-01
        • 2023-02-20
        相关资源
        最近更新 更多