Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
有没有办法在C90标准中使枚举无符号?(符合MISRA-C 2004)_C_Enums_Standards_Misra - Fatal编程技术网

有没有办法在C90标准中使枚举无符号?(符合MISRA-C 2004)

有没有办法在C90标准中使枚举无符号?(符合MISRA-C 2004),c,enums,standards,misra,C,Enums,Standards,Misra,我正在试图找到一种使枚举“未签名”的方法 那么,有没有一种方法可以使MISRA-C 2004喜欢的特定枚举无符号?没有标准的C方法来控制为枚举选择的类型。有时,您可以通过特定于实现的方式执行此操作,例如向枚举中添加一个值,强制该类型不带符号: enum { x1, x2, x3, giant_one_for_forcing_unsigned = 0x80000000; }; 但这甚至不是标准的C(因为提供的值不适合int)。不幸的是,你的运气太差了。以下是标准中的相关位: 6.

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


那么,有没有一种方法可以使MISRA-C 2004喜欢的特定枚举无符号?

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

enum {
  x1,
  x2,
  x3,
  giant_one_for_forcing_unsigned = 0x80000000;
};
但这甚至不是标准的C(因为提供的值不适合
int
)。不幸的是,你的运气太差了。以下是标准中的相关位:

6.7.2.2枚举说明符,第4段

每个枚举类型应与字符、有符号整数类型或无符号整数类型兼容。类型选择由实现定义,但应能够表示枚举的所有成员的值。枚举类型是不完整的,直到紧接着终止枚举器声明列表的
}之后,然后才完成

您最好使用
#define
而不是
enum
来生成常量:

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

uint8_t x = x2;

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

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

这也适用于C99(6.4.4.3)。如果您想要一个无符号类型,您需要一个语言扩展


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

这里有几个问题,其中存在轻微的转换错误,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) C116.2.7.7/2:

定义枚举常量值的表达式 应为具有可表示值的整型常量表达式 作为一个int。“

(2) C116.2.7.7/4:

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

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

“整数类型的复杂表达式的值只能强制转换为 一种较窄的类型,与基础结构具有相同的符号 表达式的类型。”


您可以通过包含一个足够大的值来强制它不带符号,该值不能包含在int中(根据规范)。这对于类型>=sizeof int非常简单,但unsigned char/short更复杂,需要编译器特定的打包。当然,从技术上讲,实现仍然可以将UINT_MAX表示为无符号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 })
为了更像一个标准的enum语句,这与我使用的更简单的版本略有不同,后者为最后一个enum使用了一个额外的命名参数,而不是
\uuuuu LINE\uuuu
hack(这对于出错时返回-1的函数也很有用,因为它将强制转换为U*\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或compactness的缩写外,size_t enum是最有趣的,因为它们可以用作数组索引,而无需额外的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,您可以
enumfoo:char{A,B,C}
枚举类栏:大小{X,Y,Z}

此外,要获得
枚举
声明的一些好处并产生一些未签名类型,代码可以使用以下命令

// 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;

使用0x8000000不一定会强制枚举无符号,因为这是有符号32位整数的有效表示形式。谢谢。@BobMurphy:0x8000000不能表示为带符号的32位整数。唯一可以对其进行签名的方法是
int
大于32位。@R:当然可以!是-2147483648,10号基地。这很有趣,因为您可以用32位表示的最大正整数是2147483647。这就像一个8位有符号整数,0x80是-128,而您可以表示的最大正数是0x7F=127。2的整数补码数学很有趣——不管你有多少位,正如维基百科文章所说,“似乎有一个额外的负数。”@Bob-0x8000000是一个正数,而不是负数
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]);
}
// 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)
// uint8_t = x2;
uint8_t = x2 * 1u;