enum type check in C/gcc - c

See the simple example below. When a function returning one enum is assigned to a variable of a different enum I don't get any warning even with gcc -Wall -pedantic. Why is it not possible for a C compiler to do type checking on enums? Or is it gcc specific? I don't have access to any other compiler right now to try it out..
enum fruit {
APPLE,
ORANGE
};
enum color {
RED,
GREEN
};
static inline enum color get_color() {
return RED;
}
int main() {
enum fruit ftype;
ftype = get_color();
}

This declaration:
enum fruit {
apple,
orange
};
declares three things: a type called enum fruit, and two enumerators called apple and orange.
enum fruit is actually a distinct type. It's compatible with some implementation-defined integer type; for example, enum fruit might be compatible with int, with char, or even with unsigned long long if the implementation chooses, as long as the chosen type can represent all the values.
The enumerators, on the other hand, are constants of type int. In fact, there's a common trick of using a bare enum declaration to declare int constants without using the preprocessor:
enum { MAX = 1000 };
Yes, that means that the constant apple, even though it was declared as part of the definition of enum fruit, isn't actually of type enum fruit. The reasons for this are historical. And yes, it would probably have made more sense for the enumerators to be constants of the type.
In practice, this inconsistency rarely matters much. In most contexts, discrete types (i.e., integer and enumeration types) are largely interchangeable, and the implicit conversions usually do the right thing.
enum fruit { apple, orange };
enum fruit obj; /* obj is of type enum fruit */
obj = orange; /* orange is of type int; it's
implicitly converted to enum fruit */
if (obj == orange) { /* operands are converted to a common type */
/* ... */
}
But the result is that, as you've seen, the compiler isn't likely to warn you if you use a constant associated with one enumerated type when you mean to use a different one.
One way to get strong type-checking is to wrap your data in a struct:
enum fruit { /* ... */ };
enum color { /* ... */ };
struct fruit { enum fruit f; };
struct color { enum color c; };
struct fruit and struct color are distinct and incompatible types with no implicit (or explicit) conversion between them. The drawback is that you have to refer to the .f or .c member explicitly. (Most C programmers just count on their ability to get things right in the first place -- with mixed results.)
(typedef doesn't give you strong type checking; despite the name, it creates an alias for an existing type, not a new type.)
(The rules in C++ are a little different.)

Probably most of us understand the underlying causes ("the spec says it must work"), but we also agree that this is a cause of a lot of programming errors in "C" land and that the struct wrapping workaround is gross. Ignoring add-on checkers such as lint, here's what we have:
gcc (4.9): No warning available.
microsoft cl (18.0): No warning available.
clang (3.5): YES -Wenum-conversion

gcc decided not to warn (as does clang) but icc (Intel compiler) would warn in this situation. If you want some additional type checking for enum types, you can pass your code to some static code checker software like Lint that is able to warn in such cases.
gcc decided it was not useful to warn for implicit conversions between enum types but note also that C doesn't require the implementation to issue a diagnostic in case of an assignment between two different enum types. This is the same as for the assignment between any arithmetic type: diagnostic is not required by C. For example, gcc would also not warn if you assign a long long to a char or a short to a long.

That's because enums in C are simply a group of unique integer constants, that save you from having to #define a whole bunch of constants. It's not like C++ where the enums you create are of a specific type. That's just how C is.
It's also worth noting that the actual size used to represent enum values depends on the compiler.

10 years after this question was asked, GCC can do it now:
gcc -Wextra main.c
main.c: In function ‘main’:
main.c:17:11: warning: implicit conversion from ‘enum color’ to ‘enum fruit’ [-Wenum-conversion]
17 | ftype = get_color();

An enum in C is basically handled like an integer. It's just a nicer way to use constants.
// this would work as well
ftype = 1;
You can also specify the values:
enum color {
RED=0,GREEN,BLUE
} mycolor;
mycolor = 1; // GREEN

gcc guys always have a reason not to do somthing.
Use clang with options -Wenum-conversion -Wassign-enum.

Related

Why enum exists as a type in C

I have learned from school-books that a typical definition of an enum if like this:
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
and then declare a var like:
enum weather weather_outside = rain;
My question is, if it is possible to use enumerated constants just by saying e.g. rain which keeps the integer 3, what is exactly the use and the point of having a type-like more complicated deceleration as enum weather weather_outside = rain; to have weather_outside being equal to 3 (since enum values can only be compile-time constants)? Why not just use a const or a macro for it? I am a bit confused whether enums are really necessary at all?!
Enumerations in C are largely for convenience, as they are not strongly typed. They were created to express named options, but limitations of language development and the ways people adopted them for other uses led to the current situation where they are little more than named integer values.
Enumerations support situations where we have various distinct options, such as the weather conditions you show, and want to name them. Ideally, enumerations would be strongly typed, so that rain would not be easily convertible to 3 or vice-versa; writing either int x = rain; or enum weather x = 3; would yield a warning or error from the compiler.
However, there are problems doing this. Consider when we want to write code that processes all values in an enumeration, such as:
for (enum weather i = sunny; i <= rain; i = i+1)
DoSomethingWithWeatherCondition(i);
Take a look at that update expression, i = i+1. It is perfectly natural to an experienced C programmer. (We could write i++, but that is the same thing, and it is spelled out here for illustration.) We know it updates i to the next value. But, when we think about it, what is i+1? i is an enumeration value, and 1 is an int. So we are adding two different things.
To make that work, C treated enumeration values as integers. This allows i+1 to be calculated in the ordinary way as the addition of two integers. Further, then the result is an int, and we have i = some int result, which means we have to allow assigning an int to an enum weather.
Maybe one solution to this would have been to define addition of enumeration values and integers, so that i+1 would not need to treat i as an integer; it would just be define to return the next value in the enumeration after i. But early C development did not do this. It would have been more work, and new features in programming languages were not developed all at once with foresight about what would be useful or what problems might arise. They were often developed bit-by-bit, trying out new things with a little prototype code.
So, enumeration values were integers. Once they were integers, people started using them for purposes beyond the simple original purpose. Enumerations were useful for defining constants that could be used where the compiler needed constant expressions, including array dimensions and initial values for static objects. const did not exist at the time, but it would not have served because, having defined const int x = 3; or even static const int x = 3;, we could not use that x in float array[x];. (Variable-length arrays did not exist at the time, and even now they are not available for static objects.) We also could not use x in int m = 2*x+3; when the definition of m is outside of a function (so it defines a static object). However, if x were defined as an enumeration value rather than an int, it could be used for these purposes.
This lead to enumerations being used in situations where things were not really being enumerated. For example, they are often used for bit-masks of various kinds:
enum
{
DeviceIsReadable = 1,
DeviceIsWriteable = 2,
DeviceSupportsRandomAccess = 4,
DeviceHasFeatureX = 8,
…
}
Once people started using enumerations this way, it was too late to make enumerations strongly typed and define arithmetic on them. These bit masks have to be usable with the bitwise operators |, &, and ^, not just +1. And people were using them for arbitrary constants and arithmetic on them. It would have been too difficult to redefine this part of the C language and change existing code.
So enumerations never developed as properly separate types in C.
This is not the correct syntax:
warning: unused variable 'weather_outside'
This example:
enum light {green, yellow, red};
enum weather { sunny, windy, cloudy, rain,};
enum weather wout;
wout = red; // mismatch
gives a warning with -Wextra:
implicit conversion from 'enum light' to 'enum weather'
This can help prevent errors.
const was not there in the beginning and can be a good alternative; but with an enum you do not have to assign a number - but you can:
enum {sunny, windy, cloudy,
rain = 100, snow}
This must be the most compact way to get two separated regions (0,1,2,100,101).
Your code is invalid. When you write
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
you already declared a new type called enum weather and a new variable named weather_outside. Doing enum weather weather_outside = rain; will create a new variable with the same name so all compilers I've tried emit errors on that
So the correct way is to remove the first variable definition
enum weather {
// ...
};
enum weather weather_outside = rain;
or use typedef to avoid the use of enum everywhere
typedef enum {
// ...
} weather;
weather weather_outside = rain;
The latter may not be good practice in C due to namespace pollution, and is prohibited in Linux kernel
Back to the main question.
what is exactly the use and the point of having a type-like more complicated deceleration as enum weather weather_outside = rain; to have weather_outside being equal to 3 (since enum values can only be compile-time constants)? Why not just use a const or a macro for it? I am a bit confused whether enums are really necessary at all?!
Semantically 3 doesn't mean anything, and nothing prevents rain from changing value when a new enum member is inserted before it. A named value is always better than a magic number. Besides that way it limits the range of values that weather_outside can accept. If you see or have to do weather_outside = 123 then you know there's something wrong
And to avoid using magical numbers I could also just use a macro as well #define RAIN 3
ALL CAPS ARE HARDER TO READ, and macros are generally discouraged over inline (for functions) or const (for values). But most importantly:
enum allows the debugger to show the current value as name, which is super helpful when debugging. No one knows what 123 means but they surely understand what windy represents
It may be not as useful in this example, but suppose you have a huge enum of 200 different values, how do you know what the 155th item is without counting? The middle items may also be renumbered so their values doesn't correspond to their positions anymore
I'm sure you won't be able to remember all those 200 values when you have 200 const or #define lines. Keep looking at the header file for the value is tedious. And how would you get values of const int my_weather = sunny | windy or #define RAIN (cloudy + floody)? No need to keep track of those with enum. It just works
enum {
sunny = X,
windy = Y,
my_weather = sunny | windy,
cloudy,
floody,
rain = cloudy + rain
}
enum allows you to use the constant in an array declaration
enum array_items {
SPRING,
SUMMER,
FALL,
WINTER,
NUMBER_OF_SEASONS
};
int a[NUMBER_OF_SEASONS] = { 1, 2, 3, 4};
// const int MAX_LENGTH = 4;
// int b[MAX_LENGTH]; /* doesn't work without VLA */
return a[0];
And for an example where the type may be useful, enum text_color { ... }; void set_text_color(enum text_color col); – mediocrevegetable1
I can as well call set_text_color(2) and get no warning what so ever from my compiler!
It's a limitation of C and gcc, because enum is just an integer type in C instead of a real type like in C++, so probably gcc can't do a lot of checks for it. But ICC can warn you about that. Clang also has better warnings than gcc. See:
How to make gcc warn about passing wrong enum to a function
Is there a warning for assigning an enum variable with a value out of the range of the enum?
Warn if invalid value for an enum is passed?
Typesafe enums in C?
Sure enum in C doesn't prevent you from shooting yourself like enum class in C++ but it's much better than macros or constants
Functionally the two methods are equivalent, but enums allow you to better express that something is one of several named choices. It is easier to understand "Rainy" than "3", or "South" rather than "2". It also puts a limit on which values the enumeration can take*.
Using a typedef can help in making the code less verbose:
typedef enum
{
SUNNY,
WINDY,
CLOUDY,
RAIN
} Weather;
Weather weather_outside = RAIN;
switch (weather_outside)
{
case SUNNY:
printf("It's sunny\n");
break;
case WINDY:
printf("It's windy\n");
break;
// ...
}
An additional advantage here is that the compiler may emit a warning if not all enumerated values are handled in the switch, which is wouldn't have if weather_outside was an integer.
Taking a look at function declarations, this:
void takeStep(Direction d)
is more expressive than:
void takeStep(int d)
Of course, you could write int direction, but this is using a variable name to express a type.
[*] It is technically allowed to write Weather weather_outside = 12, as enum values are integer constants, but this should look like a code smell.
Yes, at one level, using an integer variable and a set of preprocessor #defines is just about completely equivalent to using an enum. You achieve the same things: A small number of distinct values, with no necessary numeric interpretation, encoded compactly as (generally) small integers, but represented in source code by more meaningful symbolic names.
But the preprocessor is, to some extent, a kludge, and modern practice recommends avoiding its use when possible. And enums, since they are known to the compiler proper, have a number of additional advantages:
type safety — the compiler can warn you if you use values that don't belong (e.g. weather = green)
debugging — a debugger can show you the value of an enumeration as its symbolic name, not as an inscrutable number
additional warnings — the compiler can warn you if you do a switch on an enumeration but forget one of the cases
Enums are integers and can be used as constant expressions.
enum weather {
sunny,
windy,
cloudy,
rain,
} weather_outside;
int main(void)
{
int weather = cloudy;
printf("%d\n", rain);
printf("`weather`==%d\n", weather);
}
https://godbolt.org/z/939KvreEY

How to avoid pedantic warnings while using Hexadecimal in Enum?

I have an enum like this
typedef enum {
FIRST,
SECOND,
THIRD = 0X80000001,
FOURTH,
FIFTH,
} STATUS;
I am getting a pedantic warning since I am compiling my files with the option -Wpedantic:
warning: ISO C restricts enumerator values to range of 'int' [-Wpedantic]
I found that it occurs since when I convert the hex value 0X80000001 to integer it exceeds the unsigned integer limits. My purpose is to have continuous hex values as the status in the enum without this warning.
I cannot use the macros since this will defy the purpose of having the enums in the first place. What code change will avoid this warning?
Enumeration constants are guaranteed to be of the same size as (signed) int. Apparently your system uses 32 bit int, so an unsigned hex literal larger than 0x7FFFFFFF will not fit.
So the warning is not just "pedantic", it hints of a possibly severe bug. Note that -pedantic in GCC does not mean "be picky and give me unimportant warnings" but rather "ensure that my code actually follows the C standard".
It appears that you want to do a list of bit masks or hardware addresses, or some other hardware-related programming. enum is unsuitable for such tasks, because in hardware-related programming, you rarely ever want to use signed types, but always unsigned ones.
If you must have a safe and portable program, then there is no elegant way to do this. C is a language with a lot of flaws, the way enum is defined by the standard is one of them.
One work-around is to use some sort of "poor man's enum", such as:
typedef uint32_t STATUS;
#define THIRD 0X80000001
If you must also have the increased type safety of an enum, then you could possibly use a struct:
typedef struct
{
uint32_t value;
} STATUS;
Or alternatively, just declare an array of constants and use an enum to define the array index. Probably the cleanest solution but takes a little bit of extra overhead:
typedef enum {
FIRST,
SECOND,
THIRD,
FOURTH,
FIFTH,
STATUS_N
} STATUS;
const uint32_t STATUS_DATA [STATUS_N] =
{
0,
1,
0X80000001,
0X80000002,
0X80000003
};

How do I compare an enum with its zeroth value?

In c, it is not defined by the standard whether enums are signed or unsigned. However, when I try to compare an enum value to the lowest (ie 0) enumeration constant, I get the warning "pointless comparison of unsigned integer with zero." (Compiler is IAR embedded workbench.)
typedef enum
{
BAR,
BAZ
} Foo;
//later...
Foo x = (Foo)some_integral_value;
if (x >= BAR) // <- this gives me the warning
//stuff
I need to check the range of the enum, however, because it is being converted from an integral type. Is there a good way to do this that avoids the warning, which will still work if the compiler decides to change the underlying type?
In C this is a false problem.
all enumeration constants are always of type int, anyhow
conversions back and forth the enumeration type work easily with
implicit conversion, no explicit conversions (AKA cast) are necessary
nor desirable
Now to your example
enum Foo
{
BAR,
BAZ
};
//later...
Foo x = (Foo)some_integral_value;
This doesn't even compile, because in C Foo is not defined to be anything, you must use enum Foo, or provide an appropriate typedef, something like
typedef enum Foo Foo;
Perhaps you compile C code with a C++ compiler? In any case, provide a complete example that shows your problem.
It sounds like in C, all enums should have type int, but I'm leaving these suggestions around, since your compiler is either non-standard C, or compiling as C++.
If you have control over the definitions of these enums, you could just make them start at 1:
enum Foo
{
BAR = 1,
BAZ
};
You could also add a single fake negative value to force it to be signed:
enum Foo
{
NEGATIVE_PLACEHOLDER = -1,
BAR,
BAZ,
};
In C++11, you can give your enum an explicit underlying type:
enum Foo : int
{
BAR,
BAZ
};
See this page, specifically the section that says:
enum name : type { enumerator = constexpr , enumerator = constexpr , ... }
...
2) declares an unscoped enumeration type whose underlying type is fixed
It sounds like the type should always be predictable though:
Values of unscoped enumeration type are implicitly-convertible to integral types. If the underlying type is not fixed, the value is convertible first type from the following list able to hold their entire value range: int, unsigned int, long, unsigned long, long long, or unsigned long long. If the underlying type is fixed, the values can be converted to their promoted underlying type.
So, if your enum fits in an int, it should always use an int.

Enum in C is not throwing an error on invalid input

I am using gcc and I compiled this code and it should have thrown an error but it ran successfully.
enum DIRECTION {EAST,WEST,NORTH,SOUTH};
int main(void) {
enum DIRECTION currentDirection = 10;
printf("%d\n",currentDirection);
return 0;
}
OUTPUT :
10
An enums type is defined in the C99 draft standard section 6.7.2.2 Enumeration specifiers as:
Each enumerated type shall be compatible with char, a signed integer type, or an
unsigned integer type. The choice of type is implementation-defined,110) [...]
where footnote 110 says:
An implementation may delay the choice of which integer type until all enumeration constants have been seen.
the standard does not say you are not allowed to specify a value outside of those specified in the declaration of the enum although in section Annex I Common warnings it does suggest such a warning but it is not required:
A value is given to an object of an enumerated type other than by assignment of an
enumeration constant that is a member of that type, or an enumeration object that has
the same type, or the value of a function that returns the same enumerated type (6.7.2.2).
gcc will not produce a warning although clang with the -Wassign-enum flag or -Weverything flag will and it would look similar to this:
warning: integer constant not in range of enumerated type 'enum DIRECTION' [-Wassign-enum]
and you can use -Werror to make it an error.
Keith makes two interesting observations:
Using -Werror would make clang non-conforming since the code is valid C.
enum DIRECTION currentDirection = 128; has implementation defined behavior since the type could well be char.
In C an enum constant is the equivalent of an int. You can use them interchangeably.
An enum (enumeration) is a type that can hold a set of integer values specified by the user. It's a way of creating symbolic names for a small list of related values. Their purpose is to make programs clearer and more readable.
The values of an enum type are called enumerators. The enumerators are in the same scope as the enum and their values implicitly convert to integers.
A macro is meant for the preprocessor, and the compiled code has no idea about the macros you create. They have been already replaced by the preprocessor before the code hits the compiler. An enum is a compile time entity, and the compiled code retains full information about the symbol, which is available in the debugger (and other tools).
Also it's convenient to add a new symbolic name later and let the values reorder themselves. However enums in C are not strongly typed and are compatible with signed integers. So you can assign any value to an enum type variable.
// APPLE == 0, PEARS == 1, ...
enum fruits {APPLE, PEARS, BANANA};
// APPLE == 0, MANGO == 1, PEARS == 2, ...
enum fruits {APPLE, MANGO, PEARS, BANANA};
enum color {APPLE, PEACH};
enum color my_color = MANGO; // not strongly typed
enum fruits my_fruit = 7; // int -> enum fruits conversion
However enum in C++ is strongly typed.
enum class Traffic_light {red, yellow, green};
enum class Warning {green, yellow, orange, red};
Warning w = 1; // error. no int -> Warning implicit conversion
Traffic_light t = Warning::red; // type error. Warning::red is a different type

C typedef enum compilation

Does using typedef enum { VALUE_1 = 0x00, ... } typeName; have any more overhead in C (specifically, compiling using AVR-GCC for an AVR MCU) than doing typedef unsigned char typeName; and then just defining each value with #define VALUE_1 0x00?
My specific application is status codes that can be returned and checked by functions. It seems neater to me to use the typedef enum style, but I wanted to be sure that it wasn't going to add any significant overhead to the compiled application.
I would assume no, but I wasn't really sure. I tried to look for similar questions but most of them pertained to C++ and got more specific answers to C++.
An enum declaration creates an enumerated type. Such a type is compatible with (and therefore has the same size and representation as) some predefined integer type, but the compiler chooses which one.
But the enumeration constants are always of type int. (This differs from C++, where the constants are of the enumerated type.)
So typedef unsigned char ... vs. typedef enum ... will likely change the size and representation of the type, which can matter if you define objects of the type or functions that return the type, but the constants VALUE_1 et al will be of type int either way.
It's probably best to use the enum type; that way the compiler can decide what representation is best. Your alternative of specifying unsigned char will minimize storage, but depending on the platform it might actually slow down access to objects relative to, say, using something compatible with int.
Incidentally, the typedef isn't strictly necessary. If you prefer, you can use a tag:
enum typeName { Value_1 = 0x00, ... };
But then you have to refer to the type as enum typeName rather than just typeName. The advantage of typedef is that it lets you give the type a name that's just a single identifier.

Resources