0

Let's say I have an enum that only contains values up to 0xFF. I want to place that enum value into a byte variable. AFAIK enums are ints underneath (vxWorks). The machine I am coding for is big endian. I don't understand if this matters when I want to place it into a uint8_t. Which of these options will work and which is the best?

typedef enum {
  kEnum1 = 0x00,
  kEnum2 = 0x01,
  kEnum3 = 0xFF
} MyEnum;

MyEnum myenum = kEnum3;
uint8_t mybyte = 0;

// option 1, do nothing
mybyte = myenum ;

// option 2, explicit cast
mybyte = (uint8_t)myenum;

// option 3, use operators
mybyte = (myenum & 0xFF000000) >> 24;

When I test this on an online compiler, which I assume is little endian, option 3 of course does not work. But the others do. I just don't understand how big endian machines will treat these options.

pbuzz007
  • 747
  • 1
  • 5
  • 15
  • A `enum` can by any integer type which has the range to hold all values. The compiler decides which type is used. Something like `unsigned char` is also possible and used on some platforms for `enum`'s with values in the range of `0xFF>=x>=0`. – 12431234123412341234123 Dec 24 '20 at 16:40

2 Answers2

5

A cast converts a value to a new type. C 2018 6.5.4 5 says “Preceding an expression by a parenthesized type name converts the value of the expression to the unqualified version of the named type…” So if an expression of enumeration type has value 255 (0xFF), then converting it to uint8_t produces the value 255 as a value of type uint8_t. This is true for any arithmetic value that is representable in the destination type. (If the value is not representable, then there can be various results, including wrapping, trapping, undefined behavior, and truncation, depending on the types involved.)

In more detail, C 2018 specifies the results of conversions between integer types in 6.3.1.3, and an enumeration type is an integer (C 2018 6.2.5 16: “An enumeration comprises a set of named integer constant values…”). 6.3.1.3 1 says “When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.”

The bytes used to represent an enumeration or other object are not relevant to casts, because the results of casts are specified by values, not representations.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

All of your examples will work the same regardless of endianness. That is, options 1 and 2 will work fine, and option 3 won't. You can fix option 3 like:

mybyte = myenum & 0xFF;

If you want one that would break, try this one, which actually addresses the underlying memory rather than using operators:

mybyte = *(uint8_t *)&myenum;

As far as which is the best, option 1 and 2 are 100% equivalent. If you want to be extra careful, just check that myenum isn't outside of the range of a uint8_t before doing the assignment.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • This does assume that `uint8_t` is really a form of `char`, else `mybyte = *(uint8_t *)&myenum;` would be a strict aliasing violation. [It doesn't have to be](https://stackoverflow.com/questions/16138237/when-is-uint8-t-%E2%89%A0-unsigned-char/16138308). – Andrew Henle Dec 24 '20 at 16:37
  • But on a big endian machine, wouldn't a 32 bit value of 255 be stored as `0xFF000000` where as on little it would be `0x000000FF`? So therefore byte operations would be different? – pbuzz007 Dec 24 '20 at 16:42
  • No, operators work on values, not representations. – Carl Norum Dec 24 '20 at 16:43
  • Then why does it matter what way I pack a `uint16_t` into two `uint8_t`s? On big endian I need to go `byte1 = (short & 0xFF00) >> 8;` and `byte2 = short & 0x00FF`. This would be opposite for little... – pbuzz007 Dec 24 '20 at 16:48
  • Nope. It doesn't matter. The representation in memory might be different, but the operators extract the right values in both cases. – Carl Norum Dec 26 '20 at 04:28