How to set enumeration constant value out of int range?

The C99 standard requires that the expression used to determine the value of an enumeration constant has a value that is represented as int .

In section 6.7.2.2 of paragraph 2 of the C99 standard:

The expression defining the value of the enumeration constant must be an integer constant expression that has a value represented as int .

However, the types listed can be defined by the implementation for compatibility with any integer type, including those that have a range of values ​​outside of int .

In section 6.7.2.2 of paragraph 2 of the C99 standard:

Each enumerated type must be compatible with char , a signed integer type, or an unsigned integer type.

This means that although you cannot explicitly set the value of an enumeration constant outside the range of int , the value of the enumeration constant can be outside the range of int if the implementation determines the type of enumeration to be compatible with an integer type with a range outside of int .


Now I know one way to get a specific value outside the int range set for the enumeration constant: dummy enumerators.

 enum hack{ DUMMY0 = INT_MAX, DUMMY1, /* supply as many more dummy enumerators as needed */ ... /* declare desired enumerator */ FOOBAR }; 

This works thanks to section 6.7.2.2 of paragraph 3 of the C99 standard:

Enumerator c = defines its enumeration constant as the value of a constant expression.
...
Each subsequent counter without = defines its enumeration constant as the value of the constant expression obtained by adding 1 to the value of the previous enumeration constant.

Unfortunately, this only works for positive values ​​greater than INT_MAX , since the value of each subsequent counter only increases. Another caveat is the need to create, possibly, a lot of dummy counters in order to get the desired specific numbering.


This leads to the following questions:

  • Is there a way to set the enumeration constant value to a negative value outside the int range?
  • Is there a better way to set a positive value outside the int range to an enum constant?
  • As for my manual capture of an enumerator, does the C99 standard limit the number of counters that can be declared in a single enum ?
+7
c enums language-lawyer c99 enumeration
source share
3 answers

How to set enumeration constant value out of int range?

Not.

The C99 standard requires that the expression used to determine the value of an enumeration constant has a value that is represented as an int .

Yes, and the C11 standard has not changed anything.

However, the types listed can be defined by the implementation as compatible with any integer type, including those that have a range of values ​​outside of int .

Also correct.

This means that although you cannot explicitly set the value of an enumeration constant outside the range of int , the value of the constant of counting can be outside the range of int if the implementation defines an enum type that must be compatible with an integer type with a range outside of int .

This is not true, but I think you have found weakness in the wording of the standard. (Update: I do not think this is really a weakness, see below). You quoted 6.7.2.2:

An expression defining the value of an enumeration constant must be an integer constant expression that has a value represented as Int .

which, apparently, applies only when the value is determined by an explicit expression, and not like this:

 enum too_big { big = INT_MAX, even_bigger }; 

But this actually does not work, since even_bigger declared as an int constant, which obviously cannot have the value INT_MAX + 1 .

I strongly suspect that the intention is that the above announcement is illegal (violation of restrictions); 6.7.2.2 should probably be reformulated to make this clearer. (Update: now I think it is clear enough, see below.)

The gcc authors seem to agree with me:

 $ cat cc #include <limits.h> enum huge { big = INT_MAX, even_bigger }; $ gcc -c cc cc:4:5: error: overflow in enumeration values 

therefore, even if your interpretation is correct, you are unlikely to be able to write and use code that depends on it.

The workaround is to use integers (enumeration types are in any case more or less finely masked integers). The const integer object is not a constant expression, unfortunately, so you may have to resort to using a preprocessor:

 typedef long long huge_t; #define big ((huge_t)INT_MAX) #define even_bigger (big + 1) 

This assumes that long long wider than int , which is most likely not guaranteed ( int and long long can be the same size if int at least 64 bits).

The answer to your questions 1 and 2 is no; you cannot define an enumeration constant, negative or positive, outside the range of int .

As for your question 3, section 5.2.4.1 of the C11 standard says (approximately) that the compiler should support at least 1023 enumeration constants in one enumeration. Most compilers do not actually impose a fixed limit, but in any case, all constants must have values ​​in the range INT_MIN .. INT_MAX , so you are not very good. (Multiple enumeration constants of the same type can have the same value.)

(The requirement to restrict the translation is actually more complex. The compiler must support at least one program containing at least one copy of all the listed limit lists. This is a rather useless requirement, as indicated. The easiest way to satisfy the requirement given by the Standard is to avoid imposing any fixed limits.)

UPDATE:

I raised this issue in the comp.std.c Usenet newsgroup. Tim Rench raised a good point in this discussion, and now I think this:

 enum too_big { big = INT_MAX, even_bigger }; 

is a violation of a constraint that requires compiler diagnostics.

My concern was that the wording forbidding an explicit value outside the range of int :

An expression defining the value of an enumeration constant must be an integer constant expression that has a value represented as Int .

not applicable because there is no (explicit) expression. But 6.7.7.2p3 says:

Each subsequent counter without = defines its enumeration constant as the value of the expression constant obtained by adding 1 to the value of the previous enumeration constant.

(highlighted by me). Thus, there is an expression whose value should be represented as int ; it just does not appear in the source. I am not 100% comfortable with this, but I would say that the intention is clear enough.

Here's a discussion on comp.std.c.

+9
source share

Maybe I can’t say anything that Kate Thompson hasn’t told you yet.
Anyway, I will do my best.

1. Values ​​in the range of int

In ISO C99, I see in section 6.7.2.2 , paragraphs 2 and 3, the following statements:

(2) An expression defining the value of an enumeration constant must be an integer constant expression that has a value represented as int.

If you write enum T { X = (expr) } VAR , then expr is an integer in the range int , which includes at least the range -32767 .. +32767, as you can read in 5.2.4.2.1 .
Of course, paragraph (2) does not impose any restrictions on the type of identifier X.

2. Enumeration identifiers are of type int

In paragraph 3 we can read this line:

(3a) Identifiers in the list of enumerators are declared as constants of type int and can appear where it is allowed.

This is a restriction on the type of identifier. Now X is of type int .

3. Discussion of meanings

In addition, the standard says:

(3b) Enumerator c = defines its enumeration constant as the value of a constant expression.

But this proposal is now limited to (2) and (3a) .
So my interpretation of the C99 standard is this: If you write

  enum T { X = INT_MAX, BIGG} 

then BIGG is of type int (according to (3a) ).
As Kate Thompson pointed out, the value (= "enumerator constant?") Of BIGG does not come from an expression representing a value outside the range (for int ). Its value (in the mathematical sense) is equal to X + 1, since the following rule applies:

(3c) Each subsequent enumerator with no = defines its counting constant as the value of the constant expression obtained by adding 1 to the value of the previous enumeration constant.

There is no rule in the standard (on integer arithmetic) that defines the behavior of the compiler in this case. Thus, it falls into the implementation defined class ...
However, if the compiler takes this value out of range, I believe that the (mathematical) X + 1 will be converted to a value in the int range.

But the supposed behavior in (3b) is like the expression C (X + 1) == BIGG is always True . If I'm right, then I agree with Keith, and the compiler must reject the out-of- range error declaration.

4. The whole type of enumerated types

We can read this even more:

(4a) Each enumerated type must be compatible with char, a signed integer type, or an unsigned integer type.

The enum T declaration defines a new integer type .
This type is not necessarily related to the type of the expression expr , nor to the type of the counter X
This is just a different type for another thing: the integer type associated with the enum T type that we define.
(This type will be assigned to the variable VAR_T ).

An implementation may decide which type of integer is more suitable.
If expressions (e.g. expr ) have very small values, as this almost always happens, then the compiler can decide that T is of type char , for example.
If for some reason a long long is required then type T will be long long , etc.

However, this does not change the restrictions on int , which should be followed by the expression expr and the enumerator X They are int .
The only rule that binds the types expr and T :

(4b) The choice of type is determined by the implementation, but must be able to display the values ​​of all members of the enumeration.

Thus, if you have enum T { X = 0, Y = 5, Z = 9 } , then the type T can be char .
(The situation is similar to the fact that a character constant, such as 'c' , which is always of type int , is passed to the variable char : char c = 'c'; although 'c' is int , its value corresponds to the range of char ).

On the other hand, if you have enum T { X = 20202, Y = -3 } , the compiler cannot select char for T It seems that int is an ideal type for T (as for any enum type), but the compiler can choose any other integer type for T whose range contains values ​​20202 and -3.

If no value is negative, then the compiler can select the unsigned integer type.

5. Summary

Thus, we can say that:

  • There are 4 enumerations in the declaration: for expressions ( expr ), values ​​(or enumeration constants coming from expressions or just implicit), counters ( X ) and type enum ( T ).
  • The type of expressions (like expr ) is always int . The value type, apparently, should hardly be in the range of int ( INT_MAX+1 , it seems, is not allowed). The counter type is int (like X ). And enumeration types (like T ) are chosen by the implementation among all possible valid integer types, simply setting values ​​inside the declaration.
  • In the expressions we have:

    #define NN 5
    enum T { X = 0. Y = 3, Z = (NN*3) } evar;

The expression (NN * 3) + 1 is int .
The expression (Z + 1) is int .
If the compiler defines T as char ,
then the expression ((evar = Z), ++evar) is char .

+3
source share

In response to a recent Keith Thompson UPDATE :

I think you're right: the standard says ...the value of the constant expression obtained by adding 1 to the value...
Thus, the implicit expression "value" is considered as coming from the expression of a constant .

However, in order to allow only int “values”, it is necessary to jointly consider the restriction indicated in paragraph 2: The expression that defines the value of an enumeration constant ... has a value representable as an int .
Thus, values ​​(implicit expressions) must also fit into int .

Now I am convinced that the supposed limitation is that each enumerator value corresponds to an int value.

Thanks, Kate.

0
source share

All Articles