c - MISRA-C为什么忽略C强制执行的无符号到符号转换?

标签 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情形中)一致的代码?MISRA-C 2012中的规则是否发生了变化,从而影响了上述表达式?
另外,如果MISRA-C 2004要求将操作数预先转换到表达式中,或者在转换结果后转换,但是编译器错误地接受了第一和第三种形式,而这两种形式都没有,那么在什么情况下需要这样的转换?2004年版或2012年版是否允许在C Reastance的作者希望标记不重要的情况下省略类型转换(例如,允许:
uint8_t a,b,c,d;
a=b-c+d;

因为即使表达式将有符号值(b+c)添加到无符号类型的对象中,结果的强制方式也会将所有同余mod 256的结果视为等效结果?

最佳答案

MISRA-C:2004(旧)和MISRA-C:2012(当前)略有不同。前者有一个称为基础类型的概念,如果不是隐式类型提升(通过整数提升或通常的算术转换),则表达式将具有这种类型。
这一概念在MISRA-C:2012中得到了完善,并被基本类型所取代,基本类型是一组类型,在这些类型之间,隐性促销是无害的。例如,从signed charsigned int的转换并不危险-这两种类型都属于本质上有符号的组。
编写MISRA的小组是否期望C的晋升规则与C89的要求不同,编写后续修订的小组不愿意承认标准的强制性行为?
不,他们希望晋级规则是C89及以后的规定,但要认识到这些晋级规则有多危险。
我可以理解(并且实际上赞同)MISRA规则,该规则要求无符号类型上的算术必须立即将结果转换回相同的无符号类型,以防结果将以有符号或高位可能很重要的方式使用(如上面的比较)。
这确实是MISRA:s所要求的,除非隐式转换发生在同一个有符号类型上。在您的情况下,您从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可以。
问题更新后编辑。
旁注:除了不允许转换为不同有符号的大整数类型之外,MISRA-C:2004 10.1也不允许在“复杂表达式”中隐式转换为不同的底层操作数类型。其中“complex expression”是MISRA-C:2004中的一个术语,表示不是常量表达式、左值或函数返回值的所有内容。
实际上这里有多个违反10.1的行为。
如果我做对了,那么这就是工具应该说的:
(uint8_t)(a-b) > 1u不合规。整数提升将所有操作数的有向性都更改为if ((a-b) > (uint8_t)1)-。提升到不同基础类型的复杂表达式。
>不合规。整数提升将操作数的符号更改为if ((uint8_t)(a-b) > (uint8_t)1)。提升到不同基础类型的复杂表达式。左操作数的子表达式本身是兼容的,但不作为较大表达式的一部分。
>不合规。整数提升将操作数的符号更改为if ((a-b) > 1u)。提升到不同基础类型的复杂表达式。
-不合规。整数提升将操作数的标志性同时更改为if ((a-b) > 1)-。提升到不同基础类型的复杂表达式。
(讽刺的是,我那又老又烂的MISRA-C:2004 checker(LDRA)愉快地接受了所有4个表达式。但那绝对是个坏工具。)
现在之所以>是兼容的,是因为有一个对底层类型的转换,然后从那里开始通常的算术转换来保存一天。if ( (uint8_t)(a-b) > 1u)通常会被整数提升为(uint8_t)(a-b),但通常的算法会确保它最终int,这与底层类型unsigned int的符号相同。
我的结论是,问题中的“TI-checker”给出了错误的结果。
至于MISRA中这些规则背后的理由,仅仅是为了确保永远不会有任何意外的隐性促销。不知道隐式提升的程序员会写错误,应该接受教育。那些有意无意地依赖隐式提升来编写代码的程序员,会写出无法维护的代码,因此应该被解雇。
例如,一个表达式,比如uint8_t(我稍微修改了一下),有符号可能很重要。考虑a=b-c-d;。是否打算利用b=0, c=255, d=2无符号环绕,或在uint8_t上执行计算(这是实际发生的情况),或在signed int上执行计算?这三个都是可能的意图,它们可能会给出不同的结果。此外,这里还有一个隐含的左值转换-有意还是无意?

关于c - MISRA-C为什么忽略C强制执行的无符号到符号转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50430996/

相关文章:

c - 我无法使用 PIC16f88 显示来自 DS1307 的时间

当我尝试将结构中的 char* 类型的元素设置为特定字符串时,C 程序崩溃了?

在 C 中将 char 转换为具有特定格式的 uint8_t 数组

c - GNU 链接器中的部分链接是什么?

c++ - 最高操作 'constant' 没有副作用 [MISRA 2004 Rule 14.2]

转换 NULL 指针函数参数导致违反 Misra 规则 11.3

c - Polyspace 警告不转发声明 main()

c - MISRA 警告 12.4 : integer conversion resulted in truncation (negation operation)

c - STM32F746NG-发现: Program Stuck after first ADC read

c - 如何使用简单的转义序列对东欧(波兰)符号进行编码?