0

I've compiled the following code:

typedef enum E_STATUS_A
{
    STATUS_A_FIRST = 0,
    STATUS_A_SECOND
}E_STATUS_A;

typedef enum E_STATUS_B
{
    STATUS_B_FIRST = 0,
    STATUS_B_SECOND = 15
}E_STATUS_B;

E_STATUS_B bar()
{
    E_STATUS_B val = STATUS_B_SECOND;
    return val;
}

E_STATUS_A foo()
{
    int val = 0;
    val = bar();
    return val;
}

int main()
{
    foo();
    return 0;
}

After return from function bar() then val value is 15 and not STATUS_B_SECOND.

I got several questions:

  1. I assume I receive back from bar the enum STATUS_B_SECOND and val gets the value 15 because of an implicit cast from E_STATUS_B to int, correct? If so why is it happening, and why my visual studio (2019) compiler is not notifying me on that?

  2. If I change the foo code to:

E_STATUS_A foo()
{
    E_STATUS_A val = 0;
    val = bar();
    return val;
}

Then still, I get back from bar an enum of STATUS_B_SECOND however val itself is 15, is it because of an implicit cast from STATUS_B_SECOND to int? If so, I guess it doesn't matter what's the definition of val in foo, as if there won't be exact fit from the return bar value type to the val type, it will always be casted as int (or some other primitive type that is closed to the returned value from bar)?

  1. If I keep in the function foo val type as E_STATUS_A and I change the E_STATUS_B enum to
typedef enum E_STATUS_B
{
    STATUS_B_FIRST = 0,
    STATUS_B_SECOND
}E_STATUS_B;

And run the same code, I get STATUS_B_SECOND returned from foo however val itself is now of type E_STATUS_A and its value is STATUS_A_SECOND. This kind of confuses me, as from my previous code, when STATUS_B_SECOND = 15 I got just 15 as a returned value, and now I get implicit cast for E_STATUS_A type. Is that correct? If so, how is the implicit cast chooses which type to cast, and why didn't just cast to int, and moreover, why visual studio didn't raise me a warning?...

IntToThe
  • 59
  • 6
  • 1
    `enum` in C isn't really all that robust and is really just an alias for an integer type plus a set of potentially automatically assigned values. – tadman Feb 08 '21 at 08:18

1 Answers1

0

Some misconceptions:

  • There is no such thing as an "implicit cast". There are implicit conversions, or explicit conversions. An explicit conversion can be done by the programmer by using the () cast operator, meaning that a cast is always explicit and always done by the programmer.

  • Enums in general are not type safe. They consist of an enumerated type which is the type of the variable. It is guaranteed to be able to hold all the values of the enum and to be an integer type, but its size and signedness are not well-defined.

  • The contents of an enum, such as for example STATUS_A_FIRST are enumeration constants. These are guaranteed to be of type int which is signed. Therefore, implicit conversions without compiler warnings between enumerated type and int are necessary for enums to function at all.

This inconsistency between the type of an enumerated type and the type int of its enumerated constants is a known design flaw in the C language. Basically C throws type safety out the window in favour for allowing compilers to pick smaller sized integer types than int for enumerated types, where it is possible.

Then in addition, you can generally implicitly convert between all integer types in C, including enumerated types. The compiler may or may not give a diagnostic when you do. Some of those conversions are not well-defined, such as when doing from certain unsigned to signed types.


  1. I assume I receive back from bar the enum STATUS_B_SECOND and val gets the value 15 because of an implicit cast from E_STATUS_B to int, correct? If so why is it happening, and why my visual studio (2019) compiler is not notifying me on that?

For the reasons explained above, there are generally no compiler diagnostics when doing conversions between enums and int.

  1. /--/ is it because of an implicit cast from STATUS_B_SECOND to int?

Yes, enum values are treated like integers in most cases. You might get a slight bit of improved type safety if you pass values through pointers, because pointer-to-type has stricter type safety rules than plain integer variables.

If so, I guess it doesn't matter what's the definition of val in foo, as if there won't be exact fit from the return bar value type to the val type, it will always be casted as int

Note that enumerated types can store other values than those listed as the valid enumeration constants for that type. They are essentially just a glorified int with not much extra in the way of type safety.

  1. /--/ and now I get implicit cast for E_STATUS_A type

No you don't. Enumeration constants are auto-incremented if you don't initialize them explicitly. So both STATUS_A_SECOND and STATUS_B_SECOND got value 1 in this example.


You might be interested in How to create type safe enums?, though it's using somewhat advanced macro tricks and probably isn't very beginner-friendly reading.

Lundin
  • 195,001
  • 40
  • 254
  • 396