在联合中写入字节数组并从 int 读取以转换 MISRA C 中的值是否合法?

Emb*_*Dev 3 c misra unions language-lawyer

我想这之前一定有人问过,但我无法得到具体的是/否答案。

我有这个代码片段:

union integer_to_byte
{
    signed int  IntPart;
    unsigned char BytePart[2];
};

typedef union integer_to_byte I2B;

main()
{
   I2B u16VarNo;

   while(1)
   {
       // some code....
       u16VarNo.BytePart[1]= P1;

       // some more code ....
       u16VarNo.BytePart[0]= P2;

       // still more code ...
       if(u16VarNo.IntPart != 0xFFFF)
       {
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

这是在 C 中使用 Unions 的合法方式吗?从我读到的;只有最后分配的联合部分是有效的。所以“u16VarNo.BytePart[1]”不是确定的?我编写的代码按预期完美运行,但我想我会得到澄清。

TIA。

Kam*_*Cuk 5

在联合中写入字节数组并从 int 读取以转换 MISRA C 中的值是否合法?

不可以。不得使用工会。

MISRA C:2004, 18.4 - 不得使用联合。
MISRA C:2012, 19.2 - 不应使用 union 关键字

规则MISRA C:2004如下:

尽管如此,在某些情况下,在构建有效实现时需要谨慎使用联合,这是公认的。在这种情况下,只要记录了所有相关的实现定义的行为,就认为对本规则的偏离是可以接受的。这可以通过参考设计文档中编译器手册的实现部分在实践中实现。

偏差的使用可以用于 (a) 数据的打包和解包,例如在发送和接收消息时,以及 (b) 实施变体记录,前提是变体通过公共字段进行区分。

您的使用不适合这些情况。


Lun*_*din 5

形式上,unions是不允许的,尽管此规则已从 MISRA-C:2004 放宽到咨询 MISRA-C:2012。禁止的主要目的union始终是防止真正愚蠢的事情,例如在 Visual Basic 中创建“变体类型”,或者将相同的内存区域重新用于不相关的目的。

但是union用于类型双关的目的是常见的做法,特别是在嵌入式系统中,因此禁止它们也很麻烦。规则 19.2 提出了一个有效的担忧,即写入一个联合成员然后从另一个成员读取会调用未指定或实现定义的行为。如果成员不匹配,则未指定,否则实现定义,因为存在转换。

MISRA 对违反规则的进一步担忧是填充、对齐、字节序和位顺序(在位字段的情况下)。这些也是有效的问题 - 在您的具体示例中,其中许多都是潜在问题。

我的建议是这样的:

  • 仅当您知道自己在做什么时才偏离此规则。通过联合进行类型双关的有效情况是寄存器映射声明和序列化/反序列化代码。
  • 使用联合来获取某些 int 的高字节和低字节不是一个有效的用例,这很糟糕……因为它使代码不必要地不可移植而没有任何收获。假设是 16 位系统,绝对没有理由不能用可移植的位运算符替换这个联合:

    int16_t some_int = ...;
    uint8_t ms = (uint16_t)some_int >> 8;
    uint8_t ls = some_int & 0xFF;
    
    Run Code Online (Sandbox Code Playgroud)
  • 确保填充不是问题(伪代码) _Static_assert( sizeof(the_union) == sizeof(all_members)...

  • 在带有注释的源代码和 MISRA-C 实现文档中记录任何禁用填充的代码。类似#pragma pack(1)或任何您的特定编译器使用的东西。