为什么MISRA-C会忽略C的强制无签名到签名转换?

时间:2018-05-20 01:48:43

标签: c misra

C标准规定,无符号类型的值低于“int”且其值在“int”范围内,在表达式中使用时始终会提升为“signed int”。因此,给出了类似的东西:

uint8_t a,b;

if ((a-b) > (uint8_t)1) ...

减法和比较必须表现得好像所有值都转换为 输入signed int。我正在开发一个MISRA-C 2004项目,但是,有一个编译器应该根据该标准验证代码,看来MISRA的规则,至少在2004版本中,对于这种标准的促销活动是无视的。

以下表达式中,哪些表格应该在MISRA-C 2004,MISRA-C 2012,两者或两者都有效:

if ((a-b) > (uint8_t)1) ...          // Accepted by the TI compiler
if ((uint8_t)(a-b) > (uint8_t)1) ... // Accepted by the TI compiler
if ((a-b) > 1u) ...                  // Accepted by the TI compiler
if ((a-b) > 1) ...                   // Rejected by the TI compiler

如果TI编译器准确反映了MISRA-C 2004规则的要求,那么编写这些规则的团体似乎期望C的推广规则与C89所要求的不同,因为表格被拒绝了。编译器具有与第一个相同的语义,而编译器接受的第三种形式看起来像第一种形式但具有不同的角落行为,而第二种形式具有另一种不同的行为。

将所有出现的uint8_t更改为uint16_t或uint32_t似乎不会影响接受或拒绝哪些表单,但会导致第一个可接受的表单具有某些编译器与被拒绝表单匹配的语义(与uint8_t一样) ,但在其他编译器上将匹配其他两个接受的形式(然后它们将具有相同的语义)。

TI编译器是否正确地解释了MISRA-2004在接受行为因实现而异的代码时的要求,并拒绝其行为(在uint8_t情况下)在所有符合要求的实现上是否一致的代码? MISRA-C 2012中的规则是否会以一种会影响上述表达的方式发生变化?

另外,如果MISRA-C 2004要求将操作数预先转换为表达式或者后期转换结果,但是编译器错误地接受了第一个和第三个形式,这两个形式都没有,在什么情况下这样的转换需要? 2004年版或2012年版是否允许在C基础理由的作者期望签名无关紧要的情况下省略演员表(例如允许:

uint8_t a,b,c,d;
a=b-c+d;

因为即使表达式将一个有符号值(b + c)添加到无符号类型的对象中,结果也会被强制转换为将所有结果视为等效的全部结果的模式256)?

1 个答案:

答案 0 :(得分:3)

MISRA-C:2004(旧)和MISRA-C:2012(现在)略有不同。前者有一个名为底层类型的概念,如果不是用于隐式类型提升(通过整数提升或通常的算术转换),它就是表达式所具有的类型。

这个概念在MISRA-C:2012中得到了改进,取而代之的是基本类型,它是一组类型,隐式促销是无害的。例如,从signed charsigned int的转换并不危险 - 这两种类型都属于本质上已签名的组。

  

编写MISRA的小组是否期望C&C的推广规则与C89所要求的不同,编写后续修订的小组是否不愿意承认标准规定的行为?

不,他们希望促销规则符合C89及其后的规定,但要认识到这些推广规则有多危险。

  

我能理解(并且实际上是鼓掌)一个MISRA规则,要求对无符号类型的算术必须立即将结果强制转换回相同的无符号类型,以防结果将以符号或高位可能重要的方式使用[与上述比较一样]。

这确实是MISRA所要求的,除非隐式转换发生在一种相同的签名上。在您的情况下,您从uint8_t转到int,这是不正常的。要符合MISRA-C:2004标准,您可以退回到基础类型" (uint8_t)(a-b),或预先投射以彻底消除隐式转换,例如:(uint32_t)a - (uint32_t)b

此外,您不允许>将已签名的操作数与未签名的操作数进行比较。这意味着您有两个选项可以使整个表达式符合MISRA:

if ( (uint8_t)(a-b) > 1u)

if ( (uint32_t)a - (uint32_t)b > 1u)

(您可能需要添加额外的括号以满足其他MISRA规则)

  

规则的意图是((a-b)> 1)被认为是不可接受的,但((a-b)> 1u)是可以接受的

两者都不可接受,因为a-b给出了一个隐式的无符号签名转换,但它失败了。

否则,(uint8_t)(a-b) > 1仍然不行,但(uint8_t)(a-b) > 1u会没问题。

问题更新后编辑。

附注:除了不允许转换为不同签名的更大整数类型之外,MISRA-C:2004 10.1不允许隐式转换为复杂表达式中的不同基础类型的操作数"无论是。其中"复杂表达"是一个MISRA-C:2004术语,表示不是函数的常量表达式,左值或返回值。

所以这里实际上有多次违反10.1。

现在,如果我做对了,那么这就是工具应该说的:

  1. if ((a-b) > (uint8_t)1)。不合规。整数提升会将所有操作数的签名更改为->。复杂表达,提升为不同的基础类型。

  2. if ((uint8_t)(a-b) > (uint8_t)1)。不合规。整数提升会将操作数的签名更改为>。复杂表达,提升到不同的底层类型。左操作数的子表达式本身是兼容的,但不是较大表达式的一部分。

  3. if ((a-b) > 1u)。不合规。整数提升会将操作数的签名更改为-。复杂表达,提升为不同的基础类型。

  4. if ((a-b) > 1)。不合规。整数提升会将操作数的签名更改为->。复杂表达,提升为不同的基础类型。

  5. (具有讽刺意味的是,我年老,蹩脚的MISRA-C:2004格子(LDRA)欢快地接受了所有4个表达。但这绝对是一个破坏的工具。)

    现在if ( (uint8_t)(a-b) > 1u)符合的原因是因为有一个强制转换回基础类型,然后从那里通常的算术转换来节省一天。 (uint8_t)(a-b)通常会整数提升为int,但通常的算术会确保它最终unsigned int,这与基础类型uint8_t的签名相同。

    我的结论是" TI检查器"问题是给出了错误的结果。

    至于MISRA中这些规则背后的理由,它只是为了确保永远不会有任何意外的隐式促销。不知道隐式促销的程序员会编写错误并应该接受教育。编写有意和无声的代码的程序员依赖隐式促销编写不可维护的代码腐烂,应该被解雇。

    例如a=b-c-d;这样的表达式(我稍微改了一下),签名可能很重要。考虑b=0, c=255, d=2。那么打算利用uint8_t无符号环绕,还是对signed int执行计算(实际会发生什么),或者对unsigned int进行计算?所有3个都是可能的意图,它们可能会给出不同的结果。此外,这里还有一个隐含的左值转换 - 有意或无意?