在MISRA C中,将值转换为union中的字节数组并从int中读取是否合法?

在MISRA C中,将值转换为union中的字节数组并从int中读取是否合法?,c,language-lawyer,unions,misra,C,Language Lawyer,Unions,Misra,我想这一定是以前问过的,但我无法得到具体的是/否答案 我有以下代码片段: 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[

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

我有以下代码片段:

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)
       {
       }
   }
}
在C语言中使用联合是合法的吗?从我读到的;只有最后指定的并集部分有效。因此“u16VarNo.BytePart[1]”不是确定的? 我编写的代码如预期的那样完美,但我想我会把它弄清楚的

TIA.

在普通C中-仅遵循ISO C的规则,而不是MISRA添加的附加规则-显示的构造符合要求,但不严格符合要求,因为它取决于未指定的行为。在这种情况下,“未指定”意味着,允许从
u16VarNo.IntPart
读取的值给您一个毫无意义的值,但不允许它使您的程序崩溃,并且不允许编译器在假设读取永远无法执行的情况下进行优化

确切的规则是:

当值存储在联合类型的对象的成员中时,对象表示形式中与该成员不对应但与其他成员对应的字节将采用未指定的值


u16VarNo.BytePart[1]=P1
在联合类型的对象的成员中存储值。该联盟还有另外两个成员,
BytePart[0]
IntPart
imk_;它们都覆盖了对象表示的至少一个字节,该字节不对应于
BytePart[1]
(具体取决于
带符号int
的大小);当您写入
BytePart[1]
时,该字节具有未指定的值

实际结果是

u16VarNo.BytePart[1] = 0xFF;
u16VarNo.BytePart[0] = 0xFF;
您可以从
uint16VarNo.IntPart
读取,但您得到的值很可能是垃圾。特别是

assert(u16VarNo.IntPart == 0xFFFF);   // THIS ASSERTION MAY FAIL
我只是对米斯拉的附加规则略知一二,但我的印象是,这些规则完全禁止你做任何类似的事情


将两个字节的数据从外部源转换为16位有符号整数的正确方法是使用以下帮助函数:

#include <stdint.h>

int16_t be16_to_cpu_signed(const uint8_t data[static 2])
{
    uint32_t val = (((uint32_t)data[0]) << 8) | 
                   (((uint32_t)data[1]) << 0);
    return ((int32_t) val) - ((int32_t)0x10000);
}

int16_t le16_to_cpu_signed(const uint8_t data[static 2])
{
    uint32_t val = (((uint32_t)data[0]) << 0) | 
                   (((uint32_t)data[1]) << 8);
    return ((int32_t) val) - ((int32_t)0x10000);
}
#包括
int16被cpu签名(常量数据[静态2])
{

uint32_t val=((uint32_t)数据[0])联合双关,特别是使用gcc系列(或IAR、GHS、ARM和许多其他编译器)是100%好的

我认识的所有编译器都遵循脚注95

如果用于访问联合对象内容的成员不是 与上次用于在对象中存储值的成员相同 值的对象表示的适当部分是 如前所述,在新类型中重新解释为对象表示 在6.2.6中(有时称为“类型双关”的过程)。这可能是 陷阱表示法

在MISRA C中,将值转换为union中的字节数组并从int中读取是否合法

不可以。不得使用活接头

MISRA C:2004,18.4-不得使用活接头。
MISRA C:2012,19.2-不应使用union关键字

该规则如下所示:

尽管如此,人们还是认识到,在某些情况下,谨慎使用工会是构建高效实施的理想选择。在这种情况下,只要记录了所有相关实施定义的行为,对本规则的偏差是可以接受的。在实践中,可通过参考设计文档中编译器手册的实现部分

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


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

正式地说,
联合是不允许的,尽管这条规则从MISRA-C:2004到MISRA-C:2012已放宽为咨询。禁止
联合的主要目的始终是防止像创建“变体类型”这样真正愚蠢的事情与VisualBasic不同,或者为了不相关的目的重新使用相同的内存区域

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

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

我的建议是:

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

    int16_t some_int = ...;
    uint8_t ms = (uint16_t)some_int >> 8;
    uint8_t ls = some_int & 0xFF;
    
  • 通过(伪代码)
    \u Static\u assert(sizeof(联合)==sizeof(所有成员)…..确保填充不是问题。

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

这仅对2字节(16位)的本机整数有效。可能
IntPart
应为int16AFAIK类型。使用联合的合法方式是,不同类型可以存储在同一内存中:不用于类型转换。您将陷入endianne