9

If I have an enum type, like:

enum week{ sunday=0, monday, tuesday, wednesday, thursday, friday, saturday};

and I have:

enum week day;
day = saturday;
day++;

What will be the value of day?

jnd
  • 754
  • 9
  • 20
  • 1
    This seems like the kind of thing you can resolve by checking with your compiler in two seconds `printf("%d\n", ++(day = saturday));`.Of course, if you have to ask, you probably shouldn't put it in a program, anyway, since it's guaranteed to look like a bug every subsequent time you read it. – Parthian Shot May 23 '15 at 03:52
  • 3
    The fact that it compiles and executes doesn't guarantee that its behavior isn't undefined, though. My money says that the code you posted will *always* print 7, but I'd rather see a line from a specification. – fzzfzzfzz May 23 '15 at 03:56
  • Roughly related: http://stackoverflow.com/q/15303602/694576 – alk May 23 '15 at 07:18
  • when using enums, it is allways best practice to only assign declared names to the enum instance. so ++. --. 2, etc are bad programming practice and should not be used. – user3629249 May 23 '15 at 21:17

3 Answers3

4

An enumerated type is essentially a named integral value. That enumerated type is associated with an underlying integral type that can represent all the named values. That underlying integral type is required to be able to represent all the unique named values, but its actual type is implementation defined.

In this case, the numeric value of saturday will be 6. Incrementing it will give the numeric value of 7. In practice, that is unlikely to overflow the underlying integral type (int, char, unsigned char, or whatever the compiler chooses), so printing the value using the %d format will print 7.

However, there is no enumerated (named) value of type enum week with a value of 7.

If incrementing an enumerated value would overflow the underlying integral type (which is not the case here) the result is undefined. This is because the underlying integral type may be signed or unsigned, and overflowing a signed integral type gives undefined behaviour.

Theoretically, it is possible that a compiler may use an underlying type for enum week that can only represent the values 0 to 6 - in which case incrementing saturday gives undefined behaviour. In practice, AFAIK, there has yet to be any C compiler which does not choose an underlying type to be one of the standard integral types (char, int, unsigned char, unsigned, etc). All of those types can represent the numeric value of 7.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • Does the standard allow a Debug mode compile that runtime-checks the values stored in the enum to make sure they're within range? – Mark Ransom May 23 '15 at 13:39
  • The standard neither prevents nor requires such a debug mode. – Peter May 23 '15 at 14:10
  • 1
    @MarkRansom: The standard does prevent such a debug mode, or at least specifies that it's non-conforming. The value of `day` after `day++` is `7`. A conforming implementation is free to issue a compile-time warning (about that or anything else), but it's not permitted to do anything at run time other than storing the value `7` in `day`. Of course compilers are free to provide a mode that doesn't conform to the standard for the sake if imposing additional checks. – Keith Thompson May 23 '15 at 22:12
  • @KeithThompson I'd be much more interested in an answer that includes the justification for that assertion. – Mark Ransom May 24 '15 at 00:43
  • 1
    @MarkRansom: I believe [my answer](http://stackoverflow.com/a/30418074/827263) qualifies. If not, what did I miss? (I just added a new final paragraph.) – Keith Thompson May 24 '15 at 02:11
  • @KeithThompson you say "not permitted" which is a pretty strong statement. I don't see anything in your answer to support such a strong claim, although I readily admit that I may be asking a lot here. – Mark Ransom May 24 '15 at 02:37
  • 1
    @MarkRansom: `int n = 6; n++;` -- I presume we can agree that a conforming implementation is not permitted to do anything other than store `7` in `n`. `enum week day = saturday; day ++;` -- since `enum day` is compatible with some implementation-defined integer type, and any type an implementation chooses must be able to store the value `7`, it follows that a conforming implementation *must* store the value `7` in `day`. Do you see anything in the standard that permits it to do anything else? – Keith Thompson May 24 '15 at 02:50
  • @Keith. You're misinterpreting the requirement for an implementation-defined integral type underlying an `enum`. It does not mean the chosen integral type must be able to support more values than named in the enumeration, and it also doesn't mean that the chosen integral type must be a standard type (`char`, `int`, etc). There is nothing in the standard to stop an implementation using some set of bits and enforcing the range of named values. Such an `enum` would not be able to store any numeric values other than the named ones (and might - hypothetically - be useful for a "debug mode"). – Peter May 24 '15 at 06:11
  • 1
    @Peter: I don't believe I am. An enumerated type with exactly 256 members, for example, might be backed by `unsigned char` and would only be able to represent those 256 values (assuming `CHAR_BIT==8`). But an integer type that can represent `6` must be able to represent `7` because of the requirement for a pure binary binary representation. – Keith Thompson May 24 '15 at 06:23
  • There's your incorrect assumption. Although you're not alone - it is a common mistake. There is no requirement in the standard for a pure binary representation. The standard describes the behaviours of types, and effects of operations on them (so bitwise operations have defined effects) but does not mandate the representation of the types, nor does it mandate how the specified effects of operations are achieved. – Peter May 24 '15 at 07:24
  • 1
    @Peter C99 6.2.6.2 (too long to post it here) mandates that any type capable of representing 6 must also be capable of representing 7 – martinkunev Nov 25 '15 at 14:48
2

Given:

enum week {
    sunday=0, monday, tuesday, wednesday, thursday, friday, saturday
};
enum week day = saturday;
day ++;

the value of day is 7.

Quoting the 2011 ISO C standard, 6.7.2.2 paragraph 1:

Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.

_Bool is an unsigned integer type, but it doesn't meet the requirements for this particular enumeration type.

Since the value of CHAR_BIT is required to be at least 8, and the types char, unsigned char, and signed char are required to have no padding bits, the range of each of the character types must cover at least 0 through 127. Wider integer types (short, int, etc.) have ranges at least as wide as that of signed char or unsigned char. Therefore the implementation-defined type that is compatible with enum week must have a lower bound no greater than 0, and an upper bound no less than 127.

(WARNING: Language-lawyering follows.) There might be a loophole that allows for extended integer types with a wider range than _Bool but a narrower range than char. For example, I think an extended integer type with a size of 8 bits but only 3 value bits would be legal. But since integer types are required to use a binary representation, an unsigned type with 3 value bits would be able to represent values from 0 to 7, and an unsigned type with 2 value bits would not be able to represent the value of saturday. Since an enum week can hold the value 6, it must also be able to hold the value 7. In an unusual implementation it might not be able to represent the value 8, but you're unlikely encounter such an implementation.

Basically, given the requirement that integer types use a pure binary representation, any type that can represent 6 can also represent 7, though it doesn't automatically follow that it can also represent 8.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Why would a custom type with 5 value bits that can represent only 3 values (`0`, `1`, `2`) be forbidden? – Lover of Structure May 21 '23 at 05:21
  • In this regard, I just updated my question [Range of and convertibility between different enum types](https://stackoverflow.com/q/76298604/2057969). If you have the chance to have a look, that would be great. – Lover of Structure May 22 '23 at 07:03
  • 1
    Replacing my previous comment to fix a typo. @LoverofStructure If an integer type has 5 value bits, then it can represent at least 32 distinct values. If it's unsigned, those values are 0..31. If it's signed, it can represent all those values plus 31 or 32 negative values (the sign bit is not a value bit). See [N1570](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 6.2.5.2 paragraph 1. There may or may not be trap representations depending on the values of padding bits, but for each of the values 0..31 there's a combination of value and padding bits that represents that value. – Keith Thompson May 23 '23 at 00:13
1

I was only able to find a draft of the C89 specification, and I'm no C expert, so I might be misunderstanding it. But its section 3.5.2.2 says that

The identifiers in an enumerator list are declared as constants that have type int and may appear wherever such are permitted. [...] Each enumerated type shall be compatible with an integer type.

I think that means that day++ will always yield 7 here (one more than the value represented by saturday).

fzzfzzfzz
  • 1,248
  • 1
  • 12
  • 22