MISRA:对有符号整数的按位运算

时间:2013-08-27 12:33:00

标签: c misra

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

    typedef unsigned __int8   gUBYTE;

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

我怎么能解决这个错误?

6 个答案:

答案 0 :(得分:2)

变化:

gbrALL      = gbrLEFT | gbrTOP | gbrRIGHT | gbrBOTTOM

为:

gbrALL      = gbrLEFT + gbrTOP + gbrRIGHT + gbrBOTTOM

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

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

答案 1 :(得分:1)

使您的值成为一组常量,而不是将它们放在枚举中,以便您可以明确地为每个常量指定类型unsigned int

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

答案 2 :(得分:1)

显然,枚举中定义的整数最终被“签名”。您的编译器正在为您决定。请参阅:Are C++ enums signed or unsigned?

我的测试表明,adam在“枚举Y:unsigned int {.... ”中的建议仅在C ++中可以接受。

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

答案 3 :(得分:0)

通过向u 0x01u添加{{1}}来使整数无符号。

答案 4 :(得分:0)

你在使用enum获得了什么?使用位字段,常量或#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;
};

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

答案 5 :(得分: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

等等。