您可以通过包含一个足够大以使其无法放入 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};