Bitwise operation with (signed) enum value

2019-07-10 03:10发布

问题:

I am using enumerator values for flags:

typedef enum
{
    a = 0x00,
    b = 0x01u, // the u has no influence, as expected
    c = 0x02u, // the u has no influence, as expected
...
} enum_name;

volatile unsigned char* reg = SomeAddress;
*reg |= b;

According to MISRA-C:2004 bitwise operations shall not be done with a signed type. Unfortunately, My compiler IAR use signed int (or short or char) as underlying type of enums, and the only option I can find relates to the size, not the signedness ("--enum-is-int").

回答1:

According to the IAR C/C++ Development Guide for ARM, pages 169 and 211, you can define the type of your enums if you enable the IAR language extensions (-e command-line option, or Project > Options > C/C++ Compiler > Language > Allow IAR extensions in the IDE).

In particular, you should define an extra "sentinel" value, to make sure the compiler chooses the correct type. It prefers signed types, and uses smallest possible integer type, so the sentinel should be the largest positive integer the corresponding unsigned integer type can describe. For example,

typedef enum {
    /* ... */
    enum_u8_sentinel = 255U
} enum_u8;

typedef enum {
    /* ... */
    enum_u16_sentinel = 65535U
} enum_u16;

typedef enum {
    /* ... */
    enum_u32_sentinel = 4294967295UL
} enum_u32;


回答2:

It does not matter whether the underlying type is signed of unsigned, provided you only use positive values for your enum, because a positive value should have same representation as a signed or unsigned type. Standard says at 6.2.6.2 Representation of types/Integer types §5 : A valid (non-trap) object representation of a signed integer type where the sign bit is zero is a valid object representation of the corresponding unsigned type, and shall represent the same value.

So you can safely do the cast to unsigned if you want to. Anyway, if the underlying type is char (or was unsigned char), it can be (silently) promoted to int before any computation.

IMHO, MISRA-C:2004 says that bitwize operations shall not be done with a signed type, because the standard explicitely says that the representation of a negative number is implementation defined :

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; there shall be exactly one sign bit... If the sign bit is one, the value shall be modified in one of the following ways:

  • the corresponding value with sign bit 0 is negated (sign and magnitude);
  • the sign bit has the value -(2N) (two’s complement);
  • the sign bit has the value -(2N - 1) (ones’ complement).

  • Which of these applies is implementation-defined (emphasize mine)

TL/DR : If you have no warning (and you should not for a | bitwise or) you can safely use no cast at all. If you cast to the unsigned type a positive value the representatin is unchanged so you can also do the cast if you (or your corporate rules) choose to follow MISRA-C, so you can also safely cast to an unsigned type