C语言 Misra规则违反“复合表达式不应分配给具有更宽基本类型的对象”和Integral提升

pwuypxnk  于 2023-04-05  发布在  其他
关注(0)|答案(2)|浏览(195)

我有下面的函数来检查一些计数值和更新最终计数。

uint16 final_count = 0U;

uint8 count1 = 0U;
uint8 count2 = 0U;
uint8 count3 = 0U;
uint8 count4 = 0U;
  
void test(void)
{
  uint8 input = 0U;

  input =  get_input(); //Input
  count1 = get_count1(); //count1
  count2 = get_count2(); //count2
  count3 = get_count3(); //count3
  count4 = get_count4(); //count4
  
  if(input == 1U)
  {
      final_count= count1 + count2; // Both warnings Here
  }
  else if(input == 2U)
  {
      final_count= count2 + count3; // Both warnings Here
  }
  else
  {
     final_count= count1 + count2 + count3 + count4; // Both warnings Here
  }
}

在MISRA检查期间,我在if else中得到以下错误

  1. A composite expression of 'essentially unsigned' type (unsigned char) is being converted to wider unsigned type, 'unsigned short' on assignment.
  2. Integral promotion : unsigned char promoted to signed int.
    我试过用下面的方法解决
final_count = (uint16)(count1 + count2);
final_count = (uint16)(count3 + count4);
final_count = (uint16)(count1 + count2 + count3 + count4);

但是这并不能解决MISRA警告。这里到底发生了什么问题?有没有什么建议可以不用临时变量来解决这些警告?

ubof19bj

ubof19bj1#

  • 复合表达式 * 是MISRA C定义的术语,指子表达式中的算术或位运算符(MISRA C:2012 8.10.3)。(不要与ISO C术语 * 复合类型 * 混淆。)

在你的例子中,count1 + count2是一个复合表达式。操作数是uint8_t,因此本质上是无符号的。你把它赋给了一个更宽的无符号类型uint16_t,因此代码违反了规则10.6。
MISRA为规则10.6中的“复合表达式”提供了两个理由:

  • 程序员可能不知道两个操作数都隐式地提升为有符号的int
  • 一些困惑的程序员可能会有这样的误解,即仅仅因为表达式的结果存储在uint16_t中,那么加法也是使用uint16_t执行的,这当然是错误的。(MISRA似乎坚持认为这是一个常见的误解,但我并不真正相信...)

此外,规则10.8规定,不允许将复合表达式的结果强制转换为更广泛的基本类型((uint16_t)强制转换版本)。这里的目的是防止隐藏的意外,如溢出和环绕。例如,在您的特定情况下,不清楚您是否期望8位环绕。如果你期望结果的值大于255,那么你可以在255附近找到一个值。没有人(包括你自己)可以通过观察未注解的代码来判断。所以强制转换结果也不会起作用。
解决方案是在执行加法之前将变量强制转换为预期的类型:

final_count = (uint16_t)count1 + (uint16_t)count2;

顺便说一句,请养成使用标准C类型uint8_t的习惯,而不是一些私人车库的非标准uint8/u8/byte......好的工程=遵循标准和最佳实践。坏的工程=只是为了让事情变得复杂。

7vux5j2d

7vux5j2d2#

在您的示例中,您声明(使用我的注解):

uint16 final_count = 0U;   // uint16

uint8 count1 = 0U;         // uint8
uint8 count2 = 0U;         // uint8
uint8 count3 = 0U;         // uint8
uint8 count4 = 0U;         // uint8

你的三个任务是:

final_count= count1 + count2;                    // uint16 = uint8 + uint8
final_count= count2 + count3;                    // uint16 = uint8 + uint8
final_count= count1 + count2 + count3 + count4;  // uint16 = uint8 + uint8 + uint8 + uint8

这违反了MISRA C:2012 R.10.6和R.10.8
然后你会问:
这里发生的确切问题是什么?有什么解决这些警告的建议吗
在MISRA C:2012的s8.10.3和附录C中可以找到解释-您已经阅读了这本书,不是吗,并且不仅仅依赖于工具的输出?
这里的问题是一个(非常常见的)误解了C所展示的奇怪(但定义良好)的整数提升行为,特别是当使用小于intunsigned int egers时(当您可能不期望时,它会提升为signed int)-正如错误2所强调的那样。
还存在四个uint8可能溢出的真实的风险。

uint16 = uint8 + uint8;      // performed in uint8
                             // then converted to uint16

添加强制转换可以解决R.10.6的问题,但不能解决R.10.8的问题。
在您的示例中,最简单的解决方案是将四个countn 变量声明为uint16,然后所有计算都在uint16中执行,而赋值不会是 * 复合表达式 *,因此隐式转换是可以的。
(see MISRA附属机构的简介)

相关问题