Is there a way to make an enum unsigned in the C90 standard? (MISRA-C 2004 compliant)
There is no standard C way to control the type chosen for an enum
. You can do it in implementation specific ways sometimes, like by adding a value to the enumeration that forces the type to be unsigned:
enum {
x1,
x2,
x3,
giant_one_for_forcing_unsigned = 0x80000000;
};
But that's not even standard C, either (since the value provided won't fit in an int
). Unfortunately, you're pretty much out of luck. Here's the relevant bit from the standard:
6.7.2.2 Enumeration specifiers, paragraph 4
Each enumerated type shall be compatible with
char
, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration. The enumerated type is incomplete until immediately after the}
that terminates the list of enumerator declarations, and complete thereafter.
You might be better off using #define
rather than enum
to make your constants:
#define x1 0U
#define x2 1U
#define x3 2U
uint8_t x = x2;
There are several concerns here, where there is a slight potential for conversion bugs, which MISRA is trying to make you avoid:
Enum constants, that is
x1
etc in your example, are guaranteed to be of typeint
(1). But enum variables and the variable type enum is not guaranteed to be of the same type (2), if you are unlucky it is defined to be a small integer type and thereby subject to the integer promotion rules.MISRA bans implicit conversions for large integer types to smaller ones, mainly to dodge unintentional truncation of values, but also to dodge various implicit promotion rules.
Your specific MISRA-compliance error actually comes from the latter concern above, violation of rule 10.3 (3).
You can either solve this by adding an explicit cast to the "underlying type" (intended type), in this case a cast to uint8_t. Or you can solve it by never using enums at all, replace them with #defines. That might sound very radical, but keep in mind that C has no type safety whatsoever, so there is no apparent benefit of using enums apart from perhaps readability.
It is somewhat common to replace enums in this manner:
#define FALSE 0
#define TRUE 1
typedef uint8_t BOOL;
(Though the purpose in this example is mainly to make the BOOL type portable, with a guarantee to be 8 bits and never 16 bits, as might happen in case it was an enum.)
References:
(1) C11 6.2.7.7/2:
"The expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an int."
(2) C11 6.2.7.7/4:
"Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration."
(3) MISRA-c:2004 rule 10.3:
"The value of a complex expression of integer type may only be cast to a type that is narrower and of the same signedness as the underlying type of the expression."
Not only is there not a way in C90 to specify that an enum
take on an unsigned type, but in C90:
An identifier declared as an enumeration constant has type int
This also applies to C99 (6.4.4.3). If you want an unsigned type, you're looking at a language extension.
The enumeration type may be something other than int
, but the constants themselves must have int
type.