由于无符号算术运算符的环绕而导致的 MISRA 错误



我在MISRA标准中遇到了关于环绕错误的问题。我试图通过查看互联网上可用的选项来解决它,但仍然无法解决它,因为仍然无法找到一些可行的解决方案。

我提供一个简单的例子来解释我的情况。

由于 MISRA 标准,我在结果行中遇到环绕错误。无符号算术运算中的环绕。

有人可以给我一个为什么会发生它的原因以及如何满足这种情况。

多谢。

unsigned int x;
unsigned int y;
unsigned int z;
unsigned int result;
x= 0;
y = 60;
z = 60;
result = x-y +z;

正如 Dietrich Epp 正确指出的那样,代码分析器观察到,对于给定的值(在编译时已知且可见(,子表达式x-y在数学上将是负数。负值超出了无符号类型的值范围,无法表示;这种情况称为溢出。对于 C++(标准草案 n4713 中的 6.7.1/4 加上脚注 45(和 C(标准草案 n1256 中的 6.2.5/9(中的无符号整数,它有很好的定义:"无符号整数应遵循算术模 2 n 定律,其中n是值表示中的位数。

模算术"环绕"。可以将可能的值想象成一个圆圈,其中最大值(所有位集(和最小值(未设置位(相邻。与所有其他相邻值一样,我可以通过加减 1:((uint8_t)0xff+1 == 0从一个值转到另一个值,并相应地((uint8_t)0-1 == 0xff。(位模式0xff...大多数程序员都知道在通常的 2 补码中表示 -1,但当它来自计算时,它仍然可能令人惊讶。在 2 补码表示中,这种转换是自然而然的,因为0xfff... +10x10000...的,进位(前导 1(在机器表示中位的"左侧";它只是被扔掉了。

底线是:也许令人惊讶的是,分配给无符号 int 的小负值(如 -60(会导致大量数字。这就是MISRA提醒您的内容。

在您的特定情况下,使用给定的值,没有问题,因为在模算术加法中,无论值如何,加法仍然是减去的逆运算,因此0-60+60为 0。但是如果 z 是 58,则可能会出现问题,因此结果为 -2;这将导致分配给其类型可以容纳result的第二高值。例如,如果该值用于终止 for 循环,这可能会产生灾难性的后果。对于有符号整数,循环将被跳过;对于无符号整数,它可能会错误地运行很长时间。

澄清根本问题后,会出现以下问题:

  1. 如所演示的,计算是用无符号操作数很好地定义的,并且使用给定的参数,有一个不令人惊讶的结果。你是否对所有可能的参数值和组合可能出现的所有结果感到满意——也就是说,你对模算术满意吗?

    1一.是:那么问题是您是否要或必须阻止警告。从技术上讲,无事可做。任何变化都可能掩盖问题。显然,MISRA 警告可以被抑制。

    1b.如果这不可能或不希望,您的软件开发过程可能会有一种机制来限定此类警告是否可接受。

    1c.如果这是不可能的或不希望的,你将不得不(不必要地(使你的代码复杂化。一种解决方案是简单地使用 64 位有符号整数执行计算,这些整数保证能够容纳所有 32 位无符号值(假设sizeof int == 32在您的系统上(,并使用显式强制转换分配结果,以指示可能的溢出和信息丢失是故意的。

    或者,如果在数据中y < z && x + z < UINT_MAX始终成立,您可以简单地将计算重新排序为result = x + z - y;,永远不会在子表达式中遇到负值。

  2. 如果您对模算术不满意,尤其是意外大结果的可能性,则必须退后一步,重新考虑用于实际数据的数据模型。也许您必须限定数据并防止负面结果,或者您必须使用有符号整数,或者其他问题。

最新更新