声明(七)
3、枚举说明符
C语言中枚举说明符具有以下两种语法格式:
enum attribute-specifier-sequenceopt identifieropt enum-type-specifieropt {enumerator-list} enum identifier enum-type-specifieropt
其中enum是C语言关键词;attribute-specifier-sequence是属性说明符序列;identifier是标识符,也是枚举标记;enum-type-specifier是枚举类型说明符;enumerator-list是枚举器列表,枚举器之间使用逗号分隔。
枚举类型说明符具有以下语法格式:
: specifier-qualifier-list
其中specifier-qualifier-list是说明符限定符列表,说明符限定符列表不包括对齐说明符;如果存在对齐说明符,将违反限制。
枚举器具有以下两种语法格式:
enumeration-constant attribute-specifier-sequenceopt enumeration-constant attribute-specifier-sequenceopt = constant-expression
其中enumeration-constant是枚举常量;attribute-specifier-sequence是属性说明符序列;constant-expression是常量表达式。
ISO/IEC 9899:2024标准对枚举声明作了一些限制,具体如下:
-- 对于存在固定底层类型的枚举,定义枚举常量值的整数常量表达式应在固定底层类型可表示的值域范围内。如果枚举存在固定底层类型,对于没有定义常量表达式的枚举常量,其值通过前一个枚举常量加1得到,前一个枚举常量值不应是底层类型最大可表示值。
enum e1 : unsigned {a=5, b}; //a的值为5;b的值为6。 enum e2 : unsigned {c=UINT_MAX, d}; //非法,d值超出unsigned类型可表示的值域范围。
-- 对于不存在固定底层类型的枚举,定义枚举常量值的表达式必须是整数常量表达式。对于构成枚举常量值的所有整数常量表达式,必须存在一种能够表示所有枚举常量值的类型,该类型可以是char类型、标准有符号整数类型、标准无符号整数类型、扩展有符号整数类型、扩展无符号整数类型中的一种。
enum e {black, white}; //对于GCC编译器,enum e与unsigned int类型兼容。
-- 如果存在枚举类型说明符,可解释为说明符限定符列表的最长标记序列将解释为枚举类型说明符的一部分。枚举类型说明符应命名既不是枚举类型也不是位精确整数类型的整数类型。枚举的底层类型是说明符限定符列表中的类型说明符指定类型的非限定、非原子版本。
enum e {zero, one, two, three}; enum x : enum e; //非法,enum e是枚举类型。
-- 以下格式的枚举说明符
enum identifier enum-type-specifier
只能出现在以下两种情况:
enum identifier enum-type-specifier; enum identifier enum-type-specifier {enumerator-list}
其它情况下违反限制。
enum e : unsigned; //合法。 enum e : unsigned {black, white}; //合法。 struct s{ int i; enum e : unsigned ee; //非法。 }; void func(enum e : unsigned); //非法。
-- 如果包含枚举类型说明符的两个枚举说明符声明了相同类型,底层类型应是兼容类型。
enum e : unsigned; //固定底层类型是unsigned int类型。 enum e : unsigned int {a, b}; //固定底层类型是unsigned int类型。
-- 具有固定底层类型的枚举应使用枚举类型说明符显式定义。没有固定底层类型的枚举的枚举说明符不包含枚举类型说明符。
enum e1 : unsigned {a, b}; //enum e1具有固定底层类型。 enum e2 {c, d}; //enum e2不具有固定底层类型。
所有枚举都存在一个底层类型。底层类型可以使用枚举类型说明符显式指定,并且是其固定底层类型。如果没有显式指定底层类型,底层类型是枚举的兼容类型,可以是char类型、标准有符号整数类型、标准无符号整数类型、扩展有符号整数类型、扩展无符号整数类型中的一种。
enum e1 : unsigned {a, b}; //显式指定底层类型。 enum e2 {c, d}; //未显式指定底层类型。
枚举说明符中的属性说明符序列适用于枚举;枚举器中的属性说明符序列适用于枚举器。
enum [[deprecated]] e{ //此处属性与枚举说明符enum e有关。 one[[deprecated("The value is zero.")]], //此处属性与枚举器one有关。 a, b };
枚举器列表中的标识符声明为常量,可以在任何允许使用常量的地方使用。枚举器也称枚举成员,默认情况下第一个枚举成员值为0,其后枚举成员值依次加1递增。
//枚举成员值分别为0,1,2,3,4。 enum integer{ zero, one, two, three, four };
除默认值外,也可以为枚举成员指定值,其语法格式如下所示:
enumeration-constant attribute-specifier-sequenceopt = constant-expression
其中constant-expression是整型常量表达式。
指定枚举成员值时,其前面的枚举成员具有默认值,其后的枚举成员值将在指定值的基础上依次加1递增。
//枚举成员值分别为0,2,3。 enum e {red, green=2, blue};
指定枚举成员值可能在同一枚举中出现具有相同值的枚举成员。
//枚举成员red、green的值均为0。 enum e {red, green=0, blue};
同一作用域内声明的枚举常量标识符必须相互区分,并与普通标识符(ordinary identifiers)相区分;因为枚举常量标识符和普通标识符处于相同的命名空间内。
enum nationalDay{year, month, day}; enum period{day, hour, minute, second}; //非法,枚举器day重声明。 int year; //非法,标识符year重声明。
处理枚举器列表中的枚举常量时,枚举常量类型应为:
-- 先前声明类型: 如果对同一枚举常量重新声明,则该枚举常量具有先前声明类型。
(注:ISO/IEC 9899:2024标准允许:作为同一枚举类型重新声明的一部分,枚举常量可以在同一作用域内使用相同值重新定义。)
-- 枚举类型: 对于具有固定底层类型的枚举,枚举和枚举常量具有相同类型,与固定底层类型兼容。
//enum e与black、white具有相同类型, //都与unsigned类型兼容。 enum e : unsigned {black, white};
-- int类型: 如果枚举器列表不存在上述枚举常量,枚举常量也未使用=显式定义,则枚举常量类型为int类型。
//枚举常量a、b、c的类型为int类型。 enum e {a, b, c};
-- int类型: 如果枚举常量使用=显式定义,但枚举常量值可以使用int类型表示,则枚举常量类型为int类型。
//枚举常量a、b、c的类型为int类型。 enum e {a, b=USHRT_MAX, c};
-- 前一个枚举常量值加一的结果值的类型。如果发生溢出或者绕回(wraparound)前一个枚举常量加1的结果值,则枚举常量类型将采用以下任一方式:
① 能表示前一个枚举常量值加一的大小合适的有符号整数类型(不包括有符号位精确整数类型)。
② 能表示前一个枚举常量值加一的大小合适的无符号整数类型(不包括无符号位精确整数类型)。
如果前一个枚举常量是有符号整数类型,则选择有符号整数类型。如果前一个枚举常量是无符号整数类型,则选择无符号整数类型。如果没有合适大小的整数类型表示新值,那么枚举就没有可以表示其所有值的类型,因此违反约束。
对于不存在固定底层类型的枚举,枚举类型应与char类型、或者有符号整数类型、或者无符号整数类型兼容,但不能与bool类型或者位精确整数类型兼容。具体类型由实现定义,但应能表示所有枚举成员值。实现可能直至所有枚举成员可见才决定与何种整数类型兼容。
enum e {black, white}; //GCC编译器中enum e与unsigned int类型兼容。
无论是否存在标记,枚举类型(具有固定底层类型的枚举类型除外)在终止}符号前,枚举类型是不完整的;在终止}符号后,枚举类型是完整类型;对于具有固定底层类型的枚举类型,在其第一个关联枚举类型说明符结束后是完整类型。
enum weekend{ Saturday, Sunday //这里enum weekend是不完整类型。 }; //这里enum weekend是完整类型。 enum e : unsigned; //这里enum e是完整类型。 enum f{ a = sizeof(enum f), //非法,这里enum f是不完整类型。 b };
不带枚举器列表的enum identifier格式的类型说明符只能在其指定的类型完整后才能使用。
enum e1; //非法。enum e1不是完整类型。 enum e1 e11; //非法。enum e1不是完整类型。 enum e1 {a, b}; enum e1 e12; //合法。enum e1是完整类型。 enum e2 : unsigned; //合法。enum e2是完整类型。 enum e2 e21; //合法。enum e2是完整类型。 enum e2 : unsigned {c, d}; enum e2 e22; //合法。enum e2是完整类型。
没有固定底层类型的枚举类型完成时枚举成员类型是以下两种类型之一:
-- int类型: 如果所有枚举值都能使用int类型表示。
-- 枚举类型。
(注:枚举器列表处理过程中(完成前)选择的整数类型有时可能与完成后实现定义的兼容整数类型不同。)
enum e{a, b}; //i1的值为2。 int i1 = _Generic(a, int: 2, unsigned: 1, default: 0); //i2的值可能为0、1或者2。 int i2 = _Generic((enum e)a, int: 2, unsigned: 1, default: 0);
如果枚举类型具有固定底层类型,枚举成员类型是枚举类型。枚举类型与枚举底层类型兼容。左值转换后枚举类型值与底层类型值行为相同。转换成枚举类型与转换成底层类型具有相同语义。
enum e : unsigned {a, b}; //i1的值为1。 int i1 = _Generic(a, int: 2, unsigned: 1, default: 0); //i2的值为1。 int i2 = _Generic((enum e)a, int: 2, unsigned: 1, default: 0);