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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是否合法写入并集中的字节数组并从int读取以在MISRA C中转换值?相关的知识,希望对你有一定的参考价值。

我想这一定是以前被问过的,但我无法获得明确的是/否答案。

我有此代码段:

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中使用Union的合法方法吗?从我读到的只有最后分配的并集部分有效。因此“ u16VarNo.BytePart [1]”是不确定的吗?我编写的代码可以按预期运行,但是我想我应该弄清楚它。

TIA。

答案

普通 C中-仅遵循ISO C的规则,而不遵循MISRA添加的附加规则-显示的构造为conforming,但不是严格符合,因为它取决于< [未指定的行为。在这种情况下,“未指定”表示允许从u16VarNo.IntPart中读取的值根本没有任何意义,但不允许您的程序崩溃,并且不允许编译器执行以下操作:在无法执行读取的前提下进行优化。

精确的规则是C2011 section 6.2.6.1 paragraph 7

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

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

实际的结果是之后

u16VarNo.BytePart[1] = 0xFF; u16VarNo.BytePart[0] = 0xFF;

您被允许从uint16VarNo.IntPart中读取,但是您获得的值很可能是垃圾。特别是

assert(u16VarNo.IntPart == 0xFFFF); // THIS ASSERTION MAY FAIL

我只是模糊地熟悉MISRA的其他规则,但我的印象是,它们完全禁止您执行任何此类操作。


正确

将来自外部源的两个字节的数据转换为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) - 0x10000u; } 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) - 0x10000u; }
有两个功能,因为您需要知道并在代码中指定外部数据提供数据的哪个

endianness

。(这是无法依赖原始代码的另一个不相关的原因。 )您必须使用32位无符号中间值,因为常量0x10000不适用于16位寄存器。您必须将所有这些显式强制转换包括到stdint.h定宽类型中,因为否则,“常规算术转换”将使数学运算基于int值进行,这对于此代码是错误的。
¹BytePart[0]BytePart[1]是否是工会的两个独立成员的规定不明确;这是自1989 C标准最初发布以来就一直没有解决的“什么是'对象'”参数的一个实例,尽管多次尝试修正该措词。但是,假定编译器

不会

将它们视为两个单独的对象是不安全的。
另一答案
特别是使用gcc家族(或IAR,GHS,ARM和许多其他编译器)进行联合修剪是100%好的。

我知道的所有编译器都遵循脚注95。

如果用于访问联合对象内容的成员不是与上次用于在对象中存储值的成员相同,对象表示的适当部分的值是重新描述为新类型中的对象表示形式,如所述在6.2.6中(有时称为“类型校正”的过程)。这可能是陷阱表示。
另一答案
是否合法写入并集中的字节数组并从int读取以转换MISRA C中的值?

没有不得使用联合。

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

MISRA C:2004中的规则遵循:

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

[(a)打包和拆包数据时(例如,在发送和接收消息时,以及(b)实施变体记录,只要变体由一个公共字段来区分,则可以使用偏差)。>]

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

以上是关于是否合法写入并集中的字节数组并从int读取以在MISRA C中转换值?的主要内容,如果未能解决你的问题,请参考以下文章

从一个线程读取变量并从另一个线程写入的正确方法?

仅使用 Java.IO 在文件中读取/写入字节

java的byte数组最多存储多少字节?只用FileInputStream读取文件和只用FileOutputStream写入文件会出问题吗

Hadoop 是不是适合为 50GB 数据集中的 100 字节记录提供服务?

Python3 - 读写字节数据

FileStream常用的属性和方法 (转)