7

我正在尝试找到一种使枚举“未签名”的方法。

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 喜欢的特定枚举未签名?

4

5 回答 5

11

没有标准的 C 方法来控制为enum. 有时您可以通过特定于实现的方式来执行此操作,例如通过向枚举中添加一个值来强制类型为无符号:

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

但这甚至也不是标准的 C 语言(因为提供的值不适合 an int)。不幸的是,你很不走运。这是标准中的相关位:

6.7.2.2 枚举说明符,第 4 段

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

您最好使用#define而不是enum制作常量:

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

uint8_t x = x2;
于 2013-01-31T21:55:05.077 回答
7

这里有几个问题,其中有轻微的转换错误的可能性,MISRA 试图让你避免:

  • 枚举常量,即x1您的示例中的等,保证为int(1) 类型。但是 enum变量和变量类型 enum 不保证是同一类型(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:

“整数类型的复杂表达式的值只能转换为更窄且与表达式的基础类型具有相同符号的类型。”

于 2013-02-01T10:31:27.857 回答
5

不仅在 C90 中没有办法指定enum采用无符号类型,而且在 C90 中:

声明为枚举常量的标识符具有 int 类型

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

枚举类型可能不是,int但常量本身必须有int类型。

于 2013-01-31T22:02:41.087 回答
0

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

#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__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 与 C,您可以enum Foo : char { A, B, C};enum class Bar : size_t { X, Y, Z};

于 2015-03-29T10:00:42.547 回答
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;
于 2015-04-13T16:30:03.837 回答