英文:
Range of and convertibility between different enum types
问题
在哪些条件下可以将一个枚举类型转换为另一个枚举类型?
让我们考虑以下代码:
#include <stdio.h>
int main(void) {
enum enum_tag_1 { language } c = language;
enum enum_tag_2 { lawyer = 256 } d = lawyer;
d = c;
c = 2;
printf("%d\n", c);
printf("%d\n", d);
}
现在我们可以提出诸如 c = 2; 和 d = c; 这样的语句是否保证起作用的问题。
C17标准(草案,6.7.2.2)指出:
> ¶3 枚举列表中的标识符被声明为具有类型int的常量。[...]
> ¶4 每个枚举类型应与char、有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的,[] 但应能够表示枚举的所有成员的值。[...]
也就是说,似乎枚举类型只是可能由实现选择的可能是实现定义的整数类型,其大小足够大以容纳给定枚举列表的枚举值,这些枚举值都必须是int类型。
即:
enum enum_tag_1可能是一个可能由实现定义的类型,足够大以包含int0。这可能是_Bool或一个范围为{-1,0,1}的自定义类型。也就是说,赋值c = 2;不一定合法。enum enum_tag_2可能是一个可能由实现定义的类型,足够大以包含int256。这意味着它至少可以容纳范围 [0, 1, ..., 511](但不一定是 512)。因此赋值d = language(值0)始终合法。d = c;在上面的代码中是合法的,但从enum enum_tag_1赋值到enum enum_tag_2并不一定允许所有值,因为前者可能是signed而后者可能是unsigned。- 即使
enum enum_tag_1和enum enum_tag_2的枚举常量只能具有适合于类型int的值,但实现可以使这些类型变得非常大,以便可以将非常大的数字分配给它们,就像这样:
c = −9223372036854775808;
d = 18446744073709551615;
这依赖于整数类型始终具有形式为 [0, 2N-1](无符号)或 [-2N+1, 2N-1](有符号)的范围,其中 N 是值位数(C17标准,草案,6.2.6.2 ¶1-2)。 (参见这个相关问题。)
但一般情况呢?似乎enum的互相转换取决于实现选择的类型的范围,这使得答案取决于实现。
英文:
Under which conditions can one convert from one enumerated type to another?
Let's consider the following code:
#include <stdio.h>
int main(void) {
enum enum_tag_1 { language } c = language;
enum enum_tag_2 { lawyer = 256 } d = lawyer;
d = c;
c = 2;
printf("%d\n", c);
printf("%d\n", d);
}
We can now ask questions such as whether statements like c = 2; and d = c; are guaranteed to work.
The C17 standard says (draft, 6.7.2.2):
> ¶3 The identifiers in an enumerator list are declared as constants that have type int. [...]
> ¶4 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. [...]
That is, it seems that an enumerated type is simply a possibly implementation-defined integer type chosen by the implementation to be large enough for the given enumerator list, whose enumerators must all be of type int.
That is:
enum enum_tag_1is a possibly implementation-defined type large enough to contain theint0. This could be_Boolor a custom type whose range is {-1, 0, 1}. That is, the assignmentc = 2;is not necessarily legal.enum enum_tag_2is a possibly implementation-defined type large enough to contain theint256. This implies that it can at least hold the range [0, 1, ..., 511] (but not necessarily 512). Therefore the assignmentd = language(value0) is always legal.d = c;is legal in the above code, but assigning fromenum enum_tag_1toenum enum_tag_2is not necessarily allowed for all values, since the former might besignedand the latter might beunsigned.- Even though the enumeration constants for
enum enum_tag_1andenum enum_tag_2can only have values fitting into the typeint, it is legal for the implementation to makes these types so big that one can assign very large numbers to them, like this:
c = −9223372036854775808;
d = 18446744073709551615;
This relies on the fact that integer types always have ranges of the form [0, 2<sup>N</sup>-1] (unsigned) or [-2<sup>N</sup>(+1), 2<sup>N</sup>-1] (signed), where N is the number of value bits (C17 standard, draft, 6.2.6.2 ¶1-2). (See this related question.)
But what about the general case? It seems that inter-convertibility of enums depends on the ranges of the types which the implementation chooses, making the answer implementation-dependent.
答案1
得分: 3
NordicSemi在这个主题上发表了一篇观点文章,链接在这里:https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.2-dev1/tfm/docs/technical_references/design_docs/enum_implicit_casting.html
...其中我引用了以下内容:
根据C99标准1:
- §6.2.5, 16:枚举包括一组命名的整数常量值。每个不同的枚举构成一个不同的数字类型。
- §6.7.2.2, 2:定义枚举常量值的表达式必须是可表示为int的整数常量表达式。
- §6.7.2.2, 3:枚举列表中的标识符被声明为具有int类型的常量,可以出现在允许的任何地方。
- §6.7.2.2, 4:每个枚举类型都应与char、有符号整数类型或无符号整数类型兼容。类型的选择由实现定义,但必须能够表示枚举的所有成员的值。
从C99标准1的这四个引用中,可以得出以下结论:
- 枚举定义了一个新的类型,应该将其视为这种类型。
- 枚举常量必须只包含可表示为int的值。
- 枚举常量具有int类型。
- 枚举的实际类型可以在char、有符号和无符号int之间选择。编译器在可以表示枚举的所有已声明常量的类型中选择所需的类型。
结论:
- 将枚举常量分配给int始终是安全的,但最好进行强制转换以显示意图。
- 当将枚举常量从一个类型强制转换为另一个类型时,应检查常量是否适合目标类型。
- 当将整数类型(如uint32_t、int32_t等)从整数类型强制转换为枚举类型时,应检查整数的值是否是枚举常量之一。比较应该在两者中较大的类型上进行,以确保不会丢失任何信息。C整数提升应该自动为程序员执行此操作(请参阅§6.3.1.8, 1以获取规则)。
- 当将枚举类型强制转换为整数类型时,应检查枚举类型的值是否适合整数类型。具有枚举类型的变量的值不限于该类型的枚举常量。枚举常量将始终适合int。
英文:
NordicSemi has an opinion piece on this topic here: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.2-dev1/tfm/docs/technical_references/design_docs/enum_implicit_casting.html
... from which I quote:
> According to the C99 standard 1:
>
> §6.2.5, 16: An enumeration comprises a set of named integer constant
> values. Each distinct enumeration constitutes a different numerated
> type.
>
> §6.7.2.2, 2: The expression that defines the value of an enumeration
> constant shall be an integer constant expression that has a value
> representable as an int.
>
> §6.7.2.2, 3: The identifiers in an enumerator list are declared as
> constants that have type int and may appear wherever such are
> permitted.
>
> §6.7.2.2, 4: 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.
>
> From these four quotes from the C99 standard 1, the following
> conclusions can be made:
>
> - an enumeration defines a new type and should be treated as such
>
> - the enumeration constants must only contains value representable as an int
>
> - the enumeration constants have type int
>
> - the actual type of the enumeration can be between char, signed and unsigned int. The compiler chooses the type it wants among those
> that can represent all declared constants of the enumeration.
The conclusion:
> it is always safe to assign an enumeration constant to an int, but
> might be better to cast to show intent.
>
> when casting an enumeration constant to another type, it should be
> checked that the constant can fit into the destination type.
>
> when casting from an integer type (uint32_t, int32_t, etc) to an
> enumeration type, it should be checked that the integer’s value is one
> of the enumeration constants. The comparison needs to be done on the
> biggest type of the two so that no information is lost. C integer
> promotion should automatically do that for the programmer (check
> §6.3.1.8, 1 for the rules).
>
> when casting from an enumeration type to an integer type, it should be
> checked that the enumeration type value fits into the integer type.
> The value of a variable which has the type of an enumeration type is
> not limited to the enumeration constants of the type. An enumeration
> constant will always fit into an int.
答案2
得分: 2
根据我在C17标准和其他地方针对这个问题的研究(请参见原问题),我认为正确的答案如下:
- 实现始终可以将任何枚举类型设置为与
int相同。- 即使这会使枚举类型彼此可互换,各个枚举器列表必须两两不相交;也就是说,它们不能共享枚举常量。
- 枚举类型的范围必须符合上述形式,即 [0, 2N-1](无符号)或 [-2N(+1), 2N-1](有符号),只要枚举器列表中的枚举常量被涵盖,这些选择都是合法的。
- 这些范围定义了可以分配给这些
enum类型变量的值,无论在相应的枚举器列表中是否有具有这些值的int枚举常量。
- 这些范围定义了可以分配给这些
- 如果某个值恰好位于
enum B的基础整数类型的范围内,可以将enum A类型的个别值转换为enum B类型。 - 如果实现已为它们选择了相应的整数类型
type_a和type_b,其中前者的范围是后者的子集,可以将enum A类型的所有值转换为enum B类型。- 可以计算出将
enum A类型的枚举常量转换为enum B时确保能够工作的条件。细节会很繁琐,但例如,如果enum A的所有枚举常量都位于范围[-511, 511]内,如果该类型的枚举列表中有常量-1和511,则可以将所有这些常量(但不一定是enum A类型的所有值)转换为enum B。
- 可以计算出将
英文:
Given what I researched for this question in the C17 standard and elsewhere (see original question), I believe that the correct answer is the following:
- It is always legal for an implementation to make any enumerated type identical to
int.- Even though this would make the enumerated types interchangeable with each other, the various enumerator lists must be pairwise disjoint; that is, they can't share enumeration constants.
- The range of the enumerated type must be of the form above, that is [0, 2<sup>N</sup>-1] (unsigned) or [-2<sup>N</sup>(+1), 2<sup>N</sup>-1] (signed), with any of these choices being legal, as long as the enumeration constants from the enumerator list are covered.
- These ranges define what values can be assigned to variables of these
enumtypes, irrespective of whether there areintenumeration constants with those values in the respective enumerator lists.
- These ranges define what values can be assigned to variables of these
- One can convert an individual value of type
enum Ato typeenum Bif it happens to fall into the range of the underlying integer type ofenum B. - One can convert all values of type
enum Ato typeenum Bif the implementation has chosen respective integer typestype_aandtype_bfor them where the range of the former is a subset of the range of the latter.- One can work out conditions under which conversions of enumeration constants of type
enum Atoenum Bare guaranteed to work. The details would be cumbersome to write up, but for instance, if all enumeration constants ofenum Afall into the range [-511, 511], all these constants (but not necessarily all values of typeenum A) are convertible toenum Bif that type has constants-1and511in its enumerator list.
- One can work out conditions under which conversions of enumeration constants of type
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论