0

根据 MISRA 规则,我有此错误:可能无法对有符号整数执行按位运算。

    typedef unsigned __int8   gUBYTE;

    typedef gUBYTE gBORDER;
    enum {
        gbrLEFT     = 0x01,
        gbrTOP      = 0x02,
        gbrRIGHT    = 0x04,
        gbrBOTTOM   = 0x08,
        gbrALL      = gbrLEFT | gbrTOP | gbrRIGHT | gbrBOTTOM
    };

我该如何解决这个错误?

4

6 回答 6

2

改变:

gbrALL      = gbrLEFT | gbrTOP | gbrRIGHT | gbrBOTTOM

至:

gbrALL      = gbrLEFT + gbrTOP + gbrRIGHT + gbrBOTTOM

这解决了这个特定表达式中的问题。稍后在代码中,您可以使用诸如x & gbrLEFT, 之类的表达式,这也可能被标记为对有符号整数的按位运算。但是,如果x是 类型gUBYTE,则它是无符号的。另一方面,它将被提升为int,然后x & gbrLEFT在技术上是对有符号整数的操作。另一方面,这对于 MISRA 分析仪来说似乎是个问题。代码可能会对两个提升为有符号整数的无符号对象执行完全安全的按位运算,从而触发分析器的警告。因此,一个好的分析器似乎应该认识到底层对象对于按位运算是安全的。(这适用于许多按位运算。一些,例如~如果应用于已提升为 的未签名对象,则可能不安全int,具体取决于 C 实现和其他上下文。)

是这样吗?如果是这样,那么修复上面显示的一个表达式可能就足够了。

于 2013-08-27T13:26:47.543 回答
1

显然,枚举中定义的整数最终被“签名”。您的编译器正在为您决定。请参阅:C++ 枚举是有符号还是无符号?

我的测试表明亚当的建议与“ enum Y : unsigned int { .... ”仅在C ++中是可接受的。

我已经测试了我的编译器 (gcc),它已经选择了一个 unsigned int 作为我的枚举的数据类型。您可以尝试将值 0xc0000000 分配给枚举中的一项。在这种情况下,编译器被迫选择 long long 或 unsigned int。仍然是实现定义了它将为您选择的内容。

于 2013-08-27T12:53:30.683 回答
1

使您的值成为一组常量,而不是将它们放在枚举中,以便您可以显式地将类型分配给unsigned int它们中的每一个。

稍后您可以使用结果值创建一个枚举。问题是您将计算和枚举定义结合在一起。如果将它们分开,警告就会消失。

于 2013-08-27T12:48:37.470 回答
0

您从使用枚举中获得了什么?使用位域、常量或#define。枚举不应用于位字段,因为您最终会得到不是有效枚举值的值。例如 LEFT|TOP 是 0x3 并且您没有将其作为枚举值。

还有很多其他方法可以做到这一点。有些人对哪个有偏好。

#define GBR_LEFT            (1U << 0U)
#define GBR_TOP             (1U << 1U)
#define GBR_RIGHT           (1U << 2U)
#define GBR_BOTTOM          (1U << 3U)
#define GBR_MASK_ALL        (GBR_LEFT | GBR_TOP | GBR_RIGHT | GBR_BOTTOM)

或者

static const unsigned int GBR_LEFT          = (1U << 0U);
static const unsigned int GBR_TOP           = (1U << 1U);
static const unsigned int GBR_RIGHT         = (1U << 2U);
static const unsigned int GBR_BOTTOM        = (1U << 3U);
static const unsigned int GBR_MASK_ALL      = (GBR_LEFT | GBR_TOP | GBR_RIGHT | GBR_BOTTOM);

或者最好使用位域(尽管如果您要定义实际的硬件寄存器或编译器永远无法破坏的东西,则永远不要这样做)

struct gbrLocation
{
    unsigned left :1;
    unsigned top :1;
    unsigned right :1;
    unsigned bottom :1;
};

通常设置清晰的掩码也是明智的,这样您就不必在很多括号内嵌套位反转。这有助于提高代码的可读性。

于 2013-08-27T15:02:55.157 回答
0

u通过添加它们使您的整数无符号0x01u

于 2013-08-27T12:44:16.903 回答
0

诸如本例中的枚举常量int始终等价于。由于int始终是有符号的,因此您不能在按位表达式中使用枚举常量,除非您使用显式类型转换。

如前所述,替换 | 与 + 会做的伎俩。或者(虽然可读性较差),你可以写

gbrALL = (int)( (uint16_t)gbrLEFT  | 
                (uint16_t)gbrTOP   | 
                (uint16_t)gbrRIGHT | 
                (uint16_t)gbrBOTTOM)

它本来是 MISRA-C 编译器,但代码非常丑陋......

编写符合 MISRA-C 的代码的最好和最简单的方法是完全避免使用枚举。不仅枚举常数总是等于int. 但是枚举类型(具有枚举类型的变量)具有实现定义的大小和符号(C11 6.7.2.2)。也许您认为 C 是一种理性语言,并且枚举类型始终是适合保持枚举常量的类型。不是这样。它可以是无符号的,可以是小整数类型或大整数类型。这是 C 语言中的许多重大缺陷之一,如果您还没有意识到,MISRA-C 将痛苦地暴露该缺陷。

更痛苦的是,MISRA-C 强制您记录所依赖的所有实现定义的行为。这意味着您不能只通过静态分析来使包含枚举的代码符合 MISRA-C。您还必须通过引用编译器文档来写一篇关于枚举类型的所有奇怪和缺陷的文章,并说明枚举是如何在您的特定编译器上实现的。(如果编译器缺少此类文档,则它不符合 C 标准,因此会导致进一步的 MISRA 问题)。

因此,实现您想要的最佳方法是将#define所有常量作为无符号整数文字:

#define gbrLEFT  0x01u

等等。

于 2013-09-04T18:15:06.537 回答