声明(八)
4、标记
结构标记、联合标记、枚举标记位于相同的命名空间,ISO/IEC 9899:2024标准对标记的使用作了一些限制,具体如下:
-- 如果两个声明使用相同标记声明了相同类型,应使用相同关键词struct、union或者enum。
struct tagName{int money; int year; }; enum tagName{A, B, C, D}; //标记tagName发生冲突。
两个tagName标记属于同一命名空间,因此发生了名称冲突。
-- 如果同一类型的两次声明都存在成员声明或者枚举列表,其中一个声明不得嵌套在另一个声明中,而且两次声明都必须满足兼容类型的所有要求,对于结构类型和联合类型,要求相应成员必须具有相同类型,而不仅仅是兼容类型。
(注:ISO/IEC 9899:2024标准允许重新声明结构类型、联合类型、枚举类型。)
//以下相同结构类型重新声明是合法的。 struct s1{int i; double d;}; struct s1{int i; double d;}; //以下相同联合类型重新声明是合法的。 union u1{int i; double d;}; union u1{int i; double d;}; //以下相同枚举类型重新声明是合法的。 enum e1{a=1, b=2}; enum e1{b=2, a=1}; //以下相同结构类型重新声明是非法的。 //成员类型是兼容类型,不是相同类型。 struct s2{int (*ptr)[];}; struct s2{int (*ptr)[5];}; //以下相同联合类型重新声明是非法的,因为成员顺序不同。 //以下两个联合声明如果在不同编译单元内是有效的。 union u2{int i; double d;}; union u2{double d; int i;}; //以下相同枚举类型重新声明是非法的,因为枚举常量值不同。 enum e2{a=1, b=2}; enum e2{a, b};
-- enum identifier格式的枚举类型说明符如果不存在枚举器列表,应在指定类型完整后才能使用。
enum e1; //非法,enum e1是不完整类型。 enum e1 e11; //非法,enum e1是不完整类型。 enum e2 : unsigned; //合法,enum e2是完整类型。 enum e2 e21; //合法,enum e2是完整类型。 enum e3 {black, white}; //合法,enum e3是完整类型。 enum e3 e31; //合法,enum e3是完整类型。
-- struct attribute-specifier-sequenceopt identifier或者union attribute-specifier-sequenceopt identifier格式的类型说明符不应包含属性说明符序列。
struct [[deprecated]] s; //合法。 struct [[deprecated]] s{ //合法。 int i; double d; }; int func(struct [[deprecated]] s *); //非法。
同一作用域内使用相同标记声明的结构类型、联合类型或者枚举类型是相同类型。
//作用域相同,标记相同,A、B两处的struct s是相同类型。 //作用域不同,标记相同,A、C两处的struct s是不同类型。 struct s; //A struct s{ //B int i; double d; }; { struct s{ //C int i; double d; }; }
无论是否存在标记或者同一编译单元内是否存在某类型的其它声明,在定义成员列表或者枚举器列表的终止}符号前,该类型是不完整的(具有固定底层类型的枚举类型除外);在终止}符号后,该类型是完整的。
struct s; //这里struct s类型是不完整类型。 struct s{ int i; float f; }; //这里struct s类型是完整类型。 struct s; //这里struct s类型是完整类型。
具有固定底层类型的枚举类型在其第一个关联枚举类型说明符结束后是完整类型。
enum e : unsigned short; //enum e类型是完整类型,与unsigned short类型兼容。
不同作用域内或者使用不同标记声明的结构类型、联合类型或者枚举类型是不同类型。不使用标记声明的每个结构类型、联合类型或者枚举类型都是不同类型。
//不同作用域使用相同标记声明的枚举类型, //两个声明都是合法的,但声明的类型是不同枚举类型。 { enum color {black, white}; } { enum color {black, white}; } //结构成员相同,但标记不同, //两个结构类型是不同类型。 struct s1{ int i; double d; }; struct s2{ int i; double d; }; //不使用标记声明的结构类型, //两个声明都是合法的,但结构变量s1、s2具有不同结构类型。 struct { int i; double d; } s1; struct { int i; double d; } s2;
结构类型、联合类型、枚举类型可以使用以下类型说明符声明:
struct attribute-specifier-sequenceopt identifieropt {member-declaration-list} union attribute-specifier-sequenceopt identifieropt {member-declaration-list} enum attribute-specifier-sequenceopt identifieropt {enumerator-list}
成员列表(member-declaration-list)或者枚举器列表(enumerator-list)分别定义了结构内容、联合内容或者枚举内容。如果存在标识符,类型说明符还声明标识符是该类型的标记。属性说明符序列适用于声明的结构类型、联合类型或者枚举类型;此后只要声明对应类型的结构、联合或者枚举,该属性说明序列中的属性视为其属性。
struct [[deprecated]] s; //struct s类型具有[[deprecated]]属性。 struct s{ //从上次声明继承了[[deprecated]]属性。 int i; double d; }; int func(struct s *); //struct s类型具有[[deprecated]]属性。
如果声明中不存在标识符,那么在编译单元中该类型只能由其所属的声明来引用。如果声明的是typedef名,后续声明可以使用typedef名来声明指定结构类型、联合类型或者枚举类型的对象。
//这是一个无标记的联合类型,uu是具有此类型的对象。 union { int i; double d; } uu; //Color是typedef名。 typedef enum {black, white} Color; Color car;
struct attribute-specifier-sequenceopt identifier; union attribute-specifier-sequenceopt identifier; enum identifier enum-type-specifier;
以上格式的声明分别声明了结构类型、联合类型或者枚举类型,并将标识符声明为该类型的标记。属性说明符序列适用于声明的结构类型或者联合类型;此后只要声明对应类型的结构或者联合,该属性说明序列中的属性就视为其属性。
struct s; //合法。 union u; //合法。 enum e : unsigned; //合法,enum e是完整类型。
如果类型说明符struct attribute-specifier-sequenceopt identifier或者union attribute-specifier-sequenceopt identifier后面没有成员列表,也不存在其它将标识符声明为标记的可见声明,那么上述声明声明了不完整结构类型或者联合类型,并将标识符声明为该类型的标记。
如果类型说明符struct attribute-specifier-sequenceopt identifier或者union attribute-specifier-sequenceopt identifier后面没有成员列表,但存在其它将标识符声明为标记的可见声明,那么类型说明符指定的类型与其它声明声明的类型相同,而不会重新声明标记。
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是完整类型。
不完整类型只能在不需要该类型对象的大小时使用,例如:将结构类型或者联合类型声明为typedef名,声明指向结构或者联合的指针,声明返回结构或者联合的函数;但函数定义或者函数调用前,结构类型或者联合类型应是完整类型。
typedef struct s S; struct s *ptr = nullptr; struct s func(void);