3

I'm updating a physics simulation C code to use enums from long lists of #defines, but have run into an odd bug. A structure contains an enum as:

enum spec_mod_type_enum 
{ SPEC_MOD_PL=1, 
  SPEC_MOD_EXP=2,
} spec_mod_type[NXBANDS];

(NXBANDS is just a #defined value)

Due to an oversight, no key is added for -1 whilst in another file, it's modified as:

xplasma->spec_mod_type[n] = -1;

However, when compiled in both clang and gcc this results in a silent failure; the value is set to undefined, rather than -1, with unpleasant consequences. This is odd as:

  1. I was under the impression enums could be set to values outside their range.

  2. We get no warnings about this with -Wall (or -Wextra), when it seems like the exact thing enums are supposed to warn over.

Could anyone enlighten me as to why this might be happening? And/or which compiler flags would warn us about this, or at least change the default behaviour for enums to allow this set?

Toaster
  • 93
  • 2
  • 7
  • 3
    please provide a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) – m.s. Aug 06 '15 at 12:05
  • 1
    "the value is set to undefined" - what makes you think so? What is 'undefined' in this context? – keltar Aug 06 '15 at 12:08
  • "when compiled in both clang and gcc this results in a silent failure" Actually, it results in a silent success: you are allowed to store values that do not match any of the enum's constants in an enum variable. The value would be truncated to the integral type the compiler picked for your enum, but that's not a failure: the compiler did precisely what you told it to do. – Sergey Kalinichenko Aug 06 '15 at 12:09
  • [This](http://stackoverflow.com/questions/14822342/typesafe-enums-in-c) is relevant, however it requires that you change the syntax at the point of use, which I guess is impactical. – Quentin Aug 06 '15 at 12:09
  • keltar: One of the other researchers on the project found it was setting the value to just random high numbers. daskblinkenlight: But it's being set to an int (-1) and AFAIK enums are ints in memory too- so why is it *not* setting to -1? It wouldn't be a problem if it did that, and that was what I was expecting. – Toaster Aug 06 '15 at 12:11
  • 2
    @Toaster `2147483647` maybe ? This is probably what would happen when you store `-1` in an `unsigned int` (32) and it underflows. – Quentin Aug 06 '15 at 12:14
  • 1
    @Toaster different between compilers maybe, but random - I find it hard to believe. In any case, you can always look at assembly that compiler've generated, there is nothing random here. As for second part of question - you'll get warning in C++ but not in C. – keltar Aug 06 '15 at 12:19
  • Quentin: Aha! That sounds like the value he had. I'll double-check with him, but if enums are unsigned ints then that makes sense. – Toaster Aug 06 '15 at 12:19
  • 1
    @Toaster the underlying type of enums is determined by the compiler. I *think* that if you declare a negative enum member `FOO = -42`, a signed type will be picked instead, and be able to hold the `-1` correctly. – Quentin Aug 06 '15 at 12:23

2 Answers2

7

The behaviour of your program could vary from platform to platform:

The C standard allows the compiler to choose any underlying integral type for the enumerator that's capable of representing all the explicit values given: 1 and 2 in your case.

So a compiler might pick an unsigned type for your enumeration. Assigning a negative value in that case would cause wraparound modulo 2^n where n is the number of bits used to represent the unsigned type.

On the other hand, it might pick a signed type, in which case -1 would be representable.

One remedy would be to introduce a negative dummy value into your enumerator.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0

I see your issue. I think it is initialized to an undefined value -1 before it gets set to another enum value, right? I would recommend to add another enum value SPEC_MOD_UNDEFINED. Then you can simply initialize it to that at first and then set to another one later.

Also if you want to iterate over all enum values, it is practical to add a NUM_SPEC_MOD at the end, which will then take a number one higher than the last specified before.

Then you can do a

for(int i = SPEC_MOD_PL; i < NUM_SPEC_MOD;i++)

over all values. And only assign specific values to the enums if necessary, it automatically does that by itself. But seeing that you modify existing code, I see why you might want to add specific numbers. For my for-example, the numbers have to be rising integer values so that all numbers refer to a certain state.

This works because C exposes the integer representation of enumeration values directly to the programmer. Integers and enum values can be mixed freely, and all arithmetic operations on enum values are permitted. It is even possible for an enum variable to hold an integer that does not represent any of the enumeration values. In fact, according to the language definition, your code will define SPEC_MOD_PL and SPEC_MOD_EXP as constants of type int, which will only be converted (silently) to enum spec_mod_type if they are stored in a variable of that type.

(from Wikipedia Enumerated type article)

Ben
  • 2,314
  • 1
  • 19
  • 36
  • Neat idea but you're assuming the underlying enum type is an `int`. The behaviour of your loop could be undefined if SPEC_MOD_PL or NUM_SPEC_MOD cannot be converted to an `int`. – P45 Imminent Aug 06 '15 at 12:59
  • That is why I wrote that it only works if the enums are rising integer values. C exposes the integer representation of enumeration values directly to the programmer. – Ben Aug 07 '15 at 08:49
  • My neat idea also is just an extension to the actual solution described above. The actual solution is adding another enum value called SPEC_MOD_UNDEFINED to make the code always stay in the valid enum set which I think is a valid solution. I extended my answer to include why you can imply that enums are `int`. – Ben Aug 07 '15 at 09:04