英文:
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
可能是一个可能由实现定义的类型,足够大以包含int
0
。这可能是_Bool
或一个范围为{-1,0,1}的自定义类型。也就是说,赋值c = 2;
不一定合法。enum enum_tag_2
可能是一个可能由实现定义的类型,足够大以包含int
256
。这意味着它至少可以容纳范围 [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_1
is a possibly implementation-defined type large enough to contain theint
0
. This could be_Bool
or a custom type whose range is {-1, 0, 1}. That is, the assignmentc = 2;
is not necessarily legal.enum enum_tag_2
is a possibly implementation-defined type large enough to contain theint
256
. 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_1
toenum enum_tag_2
is not necessarily allowed for all values, since the former might besigned
and the latter might beunsigned
.- Even though the enumeration constants for
enum enum_tag_1
andenum enum_tag_2
can 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 enum
s 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
enum
types, irrespective of whether there areint
enumeration 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 A
to typeenum B
if it happens to fall into the range of the underlying integer type ofenum B
. - One can convert all values of type
enum A
to typeenum B
if the implementation has chosen respective integer typestype_a
andtype_b
for 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 A
toenum B
are guaranteed to work. The details would be cumbersome to write up, but for instance, if all enumeration constants ofenum A
fall into the range [-511, 511], all these constants (but not necessarily all values of typeenum A
) are convertible toenum B
if that type has constants-1
and511
in its enumerator list.
- One can work out conditions under which conversions of enumeration constants of type
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论