【问题标题】:How to assign bitfield variable to uint8_t variable without violating MISRA rules?如何在不违反 MISRA 规则的情况下将位域变量分配给 uint8_t 变量?
【发布时间】:2020-01-27 09:36:27
【问题描述】:

我有一个名为 Character 的 typedef struct

typedef struct {
    unsigned int a : 1;
    unsigned int b : 1;
    unsigned int c : 1;
    unsigned int d : 1;
    unsigned int o : 1;
    unsigned int p : 1;
    unsigned int q : 1;
    unsigned int x : 1;
} Character;

static Character tempChar;

void writeVar(const uint8_t *pData)
{
    tempChar.a = pData[0] >> 5;
    ...
}

当我尝试将 uin8_t 变量(值为 01)分配给这些位域之一时,我违反了 MISRA 规则 10.6,该规则指出:

复合表达式的值不应分配给 具有更广泛基本类型的对象

有没有办法在不违反 MISRA C 的情况下将位域分配给 uint8_t?

【问题讨论】:

  • 你是怎么得到这个警告的?如何编译代码?
  • 我正在使用一个工具来进行静态分析。我必须遵守 MISRA C。
  • 你试过tempChar.a = (uint8_t)(pData[0] >> 5);吗?
  • 是的,我做到了。它没有帮助。
  • 尽量避免int促销:tempChar.a = ((unsigned)pData[0]) >> 5;

标签: c bit-fields misra uint8t


【解决方案1】:

如果需要,表达式pData[0] >> 5 中的两个操作数将是promotedintpData[0] 会发生这种情况)。

表达式的结果是int

促销和从intunsigned int 的转换虽然在正常情况下完全有效且正常,但足以让非常严格的 MISRA 抱怨。

简单的解决方案(如 cmets 所示)是使用强制转换将pData[0] 显式转换为unsigned int

【讨论】:

  • 哦,更简单。为什么我首先想到了union! :(
  • 使用~pData[0] >> 5 运行相同的代码。然后,~(uint32_t)pData[0] >> 5。然后抱怨 MISRA...除非您当然认为将值 -1 存储在 :1 位域中是个好主意。
【解决方案2】:
tempChar.a = pData[0] >> 5;

在此5 是一个有符号整数常量。您应该使用5U 作为无符号常量

另外,右移操作的结果将是int,因此您需要将结果类型转换回unsigned int

tempChar.a = (unsigned int) (pData[0] >> 5U);

【讨论】:

    【解决方案3】:

    这里的核心问题与 MISRA 无关,而是试图将值存储在位域的特定槽中。您无法知道您的位域布局实际上是如何在内存中结束的,因为 C 标准中没有定义。

    您的位域是在 MS 字节还是 LS 字节中分配 8 个值位?是否按照字节顺序排列?什么是位顺序?没人知道。第 1 步是去掉位域。

    第 2 步是删除任何 unsigned int 并使用 uint16_t/uint32_t


    特别是对于 MISRA-C 10.6,禁止隐式转换为更广泛类型的规则总是被误导。 MISRA 用于此规则的基本原理是防止人们编写像 uint32_t u32 = u16a + u16b; 这样的代码,并认为 =u32 操作数以某种方式神奇地意味着该操作将在 32 位而不是 16 位上执行。但是在8/16位系统,以16位运算执行,可能会出现溢出/回绕。

    碰巧的是,对有符号类型进行位移总是是一个非常糟糕的主意。 pData[0] 被隐式提升为已签名的 int。还有其他 MISRA 规则处理此问题,而不是您引用的规则。

    不管 MISRA 是什么,您都应该养成始终对无符号类型执行轮班的习惯。 “在这种情况下并不危险”是一个黯淡的理由。这意味着总是写(uint32_t)pData[0] >> 5 并且演员应该被应用之前转移而不是之后。这消除了关于未定义行为左移和可能的算术右移等的所有不确定性。让优化器从那里担心操作数的实际使用大小。

    【讨论】:

      【解决方案4】:

      出于这个原因,我发现 MISRA C 过于复杂。 无论如何,您并没有说要直接分配它。如果是这种情况,您可以采取以下措施:

      typedef union {
          
          struct {
              unsigned int a : 1;
              unsigned int b : 1;
              unsigned int c : 1;
              unsigned int d : 1;
              unsigned int o : 1;
              unsigned int p : 1;
              unsigned int q : 1;
              unsigned int x : 1;
          };
          
          uint8_t u8Value;
          
      } Character;
      

      并通过访问tempChar.u8Value 而不是位字段来设置这些值。例如,

      tempChar.u8Value |= (1 << 0);
      

      会将tempChar.a 设置为1

      这仍然会在相同程度上保持代码的整洁(可读性)。例如,

      if(1 == tempChar.a)
      { 
          // Some code
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-16
        相关资源
        最近更新 更多