我正试图找到一种让枚举“无符号”的方法。
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*/
那么,有没有办法让MISRA-C 2004想要的特定枚举无符号?
答案 0 :(得分:10)
没有标准的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;
答案 1 :(得分: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:
“每个枚举类型应与char,一个有符号整数兼容 类型或无符号整数类型。类型的选择是 实现定义,但应能够代表 枚举的所有成员的值。“
(3)MISRA-c:2004规则10.3:
“整数类型的复杂表达式的值只能转换为 一种比底层更窄且具有相同符号的类型 表达式的类型。“
答案 2 :(得分:5)
在C90中,不仅没有办法指定enum
采用无符号类型,而且在C90中:
声明为枚举常量的标识符具有int
类型
这也适用于C99(6.4.4.3)。如果您想要一个无符号类型,那么您正在查看语言扩展名。
枚举类型可能不是int
,但常量本身必须有int
类型。
答案 3 :(得分:0)
您可以通过包含一个足够大的值使其无法符合int(每个规范)来强制它无符号。这对于类型&gt; = 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;
}
更像是标准的枚举语句,这与我使用的更简单的版本略有不同,后者为最后一个enum而不是__LINE__
hack采用了额外的命名参数(这对于返回的函数也很有用) -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};
答案 4 :(得分:0)
除了@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知道的情况下进行转换。
// uint8_t = x2;
uint8_t = x2 * 1u;