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.