2013-01-31 16 views
2

我试图找到一种方法来使枚举“无符号”。有没有办法使C90标准(符合MISRA-C 2004)无符号枚举

enum{ 
    x1 = 0, 
    x2, 
    x3 
}; 
uint8_t = x2; /* <--- PC-LINT MISRA-C 2004 will complain about mixing signed and unsigned here */ 

当然,我可以添加一个类型转换摆脱错误的,这是耗时且容易出错。

uint8_t = (uint8_t)x2; /* This works, but is a lot of extra work over the course of 1000s of lines of code*/ 

那么,有没有办法让一个特定的枚举unsigned MISRA-C 2004会喜欢?

+1

这是来自PC-LINT的一个愚蠢的警告。 'x2'的值是静态已知的,并且已知适合'uint8_t'的范围,所以类型是无关紧要的。同样愚蠢的是发出'char x = 1LL;'的警告。 –

+0

@R ..问题在于MISRA与PC-Lint无关。 'uint8_t x = 1;'不符合MISRA-C,它不需要在已签名和未签名类型之间进行隐式转换。 – ouah

+2

这是MISRA关于枚举的一个问题:枚举常量和枚举变量不一定具有相同的类型,因此它们不一定是兼容的。这是C语言中许多奇怪的不一致之处,也不是MISRA的错,它们只是试图避开语言的所有这些非理性缺陷。 – Lundin

回答

9

没有标准的C方式来控制enum所选的类型。您可以通过添加一个值,以强制类型是无符号枚举做到在实施的具体方式有时,如:

enum { 
    x1, 
    x2, 
    x3, 
    giant_one_for_forcing_unsigned = 0x80000000; 
}; 

但是这还没有标准C,或者(因为所提供的价值将不适合在int)。不幸的是,你几乎不走运。下面是从标准的相关位:

6.7.2.2枚举符,第4款

每个枚举类型应char,有符号整数类型,或者一个无符号整数类型兼容。类型的选择是实现定义的,但应能够表示枚举的所有成员的值。枚举类型是不完整的,直到紧接在终止枚举器声明列表之后并在其后完成。

你可能会更好使用#define而不是enum,使您的常量:

#define x1 0U 
#define x2 1U 
#define x3 2U 

uint8_t x = x2; 
+0

使用0x80000000不一定会强制枚举为无符号,因为这是一个有符号的32位整数的有效表示。 –

+0

卡尔 - 我宁愿认为情况就是如此。谢谢。 – Tom

+2

@BobMurphy:0x80000000不能表示为一个带符号的32位整数。它可以被签名的唯一方法是如果int大于32位。 –

5

不仅是有没有在C90的方式来指定一个enum采取对无符号类型,但在C90:

声明为枚举常数的标识符的类型为int

这也适用于C99(6.4.4.3)。如果你想要一个无符号类型,你正在寻找一个语言扩展。

枚举类型可能比其他0​​东西,但常数本身必须具有int类型。

6

有一些关注这里,那里是转换错误轻微的潜力,这MISRA正试图使你避免:

  • 枚举常数,即x1等在你的榜样,是保证是类型int(1)。但枚举变量和变量类型枚举不能保证是相同的类型(2),如果你不幸它被定义为一个小整数类型,从而服从整数提升规则。

  • MISRA禁止将大整数类型的隐式转换转换为较小的转换类型,主要是为了避免无意中截断值,同时也避开各种隐式升级规则。

您的具体MISRA遵从性错误实际上来自上述后者,违反规则10.3(3)。

您可以通过向“基础类型”(预期类型)添加明确转换来解决此问题,在这种情况下,转换为uint8_t。或者你可以通过永远不使用枚举来解决它,用#defines替换它们。这听起来可能非常激进,但请记住,C没有任何类型安全性,因此使用枚举或许可读可读性没有明显的好处。

这是有点共同以这种方式,以取代枚举:

#define FALSE 0 
#define TRUE 1 
typedef uint8_t BOOL; 

(尽管在这个例子中,目的主要是使BOOL类型的便携式,具有保证是8位,从未16位,在可能的情况下发生的,这是一个枚举)


参考文献:

(1)C11 6.2.7.7/2:

“定义枚举常量 的值的表达式应该是一个整型常量表达式,其值可表示为 作为int。”

(2)C11 6.2.7.7/4:

“每一枚举类型应与炭,有符号整数 类型,或一个无符号整数类型兼容类型的选择是 实现定义的,但是应该能够表示枚举的所有成员的 值。“

(3)MISRA-C:2004规则10.3:

“整数类型的复杂表达式的值可仅被转换为 一个类型,它是窄相同的符号性的和作为底层 表达式的类型“。

0

您可以通过包含足够大的值以使其无法放入int(每个规范)来强制它被无符号签名。这对于> = sizeof int类型非常简单,但unsigned char/short更加复杂,并且需要编译器特定的打包。当然,实现技术上仍然可以将UINT_MAX表示为一个无符号长整数......但这并不是我见过的。

#include <stdio.h> //only included for printf example 
#include <limits.h> 
#include <stdint.h> 

/** set up some helper macros **/ 
#ifdef _MSC_VER 
    #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) 
#else /* for gcc, clang, icc and others */ 
    #define PACK(...) __VA_ARGS__ __attribute__((__packed__)) 
#endif 
#define _PASTE(x,y) x ## y 
#define PASTE(x,y) _PASTE(x,y) 

/* __LINE__ added for semi-unique names */ 
#define U_ENUM(n, ...) \ 
    enum n { __VA_ARGS__ , PASTE(U_DUMMY , __LINE__) = UINT_MAX } 
#define UL_ENUM(n, ...) \ 
    enum n { __VA_ARGS__ , PASTE(UL_DUMMY , __LINE__) = ULONG_MAX } 
#define SZ_ENUM(n, ...) /* useful for array indices */ \ 
    enum n { __VA_ARGS__ , PASTE(SZ_DUMMY , __LINE__) = SIZE_MAX } 
#define ULL_ENUM(n, ...) \ 
    enum n { __VA_ARGS__ , PASTE(ULL_DUMMY , __LINE__) = ULLONG_MAX } 
#define UC_ENUM(n,...) \ 
    PACK(enum n { __VA_ARGS__ , PASTE(UC_DUMMY , __LINE__) = UCHAR_MAX }) 
#define US_ENUM(n,...) \ 
    PACK(enum n { __VA_ARGS__ , PASTE(US_DUMMY , __LINE__) = USHRT_MAX }) 

下面是一个检查,看看它按预期工作:

typedef UC_ENUM(,a) A_t; 
typedef US_ENUM(,b) B_t; 
typedef U_ENUM(,c) C_t; 
typedef UL_ENUM(,d) D_t; 
typedef ULL_ENUM(,e) E_t; 
typedef SZ_ENUM(,e) F_t; 
int main(void) { 
    printf("UC %d,\nUS %d,\nU %d,\nUL %d,\nULL %d,\nSZ %d,\n",sizeof(A_t), 
    sizeof(B_t),sizeof(C_t),sizeof(D_t),sizeof(E_t),sizeof(F_t)); 
    return 0; 
} 

更像一个标准的枚举声明这比简单的版本我用,这需要一个额外的命名有少许不同参数最后枚举,而不是__LINE__黑客(这也是返回-1错误功能非常有用,因为它会转换为U * _MAX) 下面是该版本的外观:

#define U_ENUM(n, err, ...)  enum n { __VA_ARGS__ , err = UINT_MAX } 
#define UL_ENUM(n, err, ...)  enum n { __VA_ARGS__ , err = ULONG_MAX } 
#define ULL_ENUM(n,err, ...)  enum n { __VA_ARGS__ , err = ULLONG_MAX} 
#define SZ_ENUM(n, err, ...)  enum n { __VA_ARGS__ , err = SIZE_MAX } 
#define UC_ENUM(n, err, ...) PACK(enum n { __VA_ARGS__ , err = UCHAR_MAX }) 
#define US_ENUM(n, err, ...) PACK(enum n { __VA_ARGS__ , err = USHRT_MAX }) 

除了在char中包含枚举或紧凑性之外,size_t枚举最有趣,因为它们可以用作数组索引而无需额外的MOV指令。

typedef SZ_ENUM(message_t,MSG_LAST,MSG_HELLO,MSG_GOODBYE,MSG_BAD) message_t; 
static const char *messages[]={"hello","goodbye","bad message"}; 
void printmsg(message_t msg){ 
    if (msg > MSG_BAD) msg = MSG_BAD; 
    (void) puts(messages[msg]); 
} 

注意:如果使用C++ 11 VS C,可以enum Foo : char { A, B, C};enum class Bar : size_t { X, Y, Z};

0

在addtion到@Carl's answer,得到一些的enum声明的好处,并导致一些未签名的类型,代码可以使用下面的。

// Form values 0, 5, 6 
enum { 
x1, 
x2 = 5, 
x3 
}; 

// Form values 0u, 5u, 6u 
#define ux1 (1u * x1) 
#define ux2 (1u * x2) 
#define ux3 (1u * x3) 

这可能无助于int范围之外的枚举常量。

当然代码可以做OP转换,而不是OP知道。

// uint8_t = x2; 
uint8_t = x2 * 1u; 
相关问题