声明(三)
三、类型说明符
C语言中类型说明符包括:void、char、short、int、long、float、double、signed、unsigned、_BitInt(constant-expression)、bool、_Complex、_Decimal32、_Decimal64、_Decimal128、原子类型说明符、结构说明符、联合说明符、枚举说明符、typedef名、typeof说明符。
除非类型推断,否则每个声明的声明说明符中以及每个成员声明和类型名的说明符限定符列表中都必须存在至少一个类型说明符。每个类型说明符列表应是以下多集之一(当一个项存在多个多集时使用逗号分隔。);类型说明符可以以任意顺序出现,也可以与其它声明说明符一起使用。
- void
- char
- signed char
- unsigned char
- short, signed short, short int, signed short int
- unsigned short, unsigned short int
- int, signed, signed int
- unsigned, unsigned int
- long, signed long, long int, signed long int
- unsigned long, unsigned long int
- long long, signed long long, long long int, signed long long int
- unsigned long long, unsigned long long int
- _BitInt(constant-expression), signed _BitInt(constant-expression)
- unsigned _BitInt(constant-expression)
- float
- double
- long double
- _Decimal32
- _Decimal64
- _Decimal128
- bool
- float _Complex
- double _Complex
- long double _Complex
- 原子类型说明符
- 结构说明符
- 联合说明符
- 枚举说明符
- typedef名
- typeof说明符
如果实现不支持复数,不应使用_Complex类型说明符;如果实现不支持十进制浮点类型,不应使用_Decimal32、_Decimal64和_Decimal128类型说明符。
_BitInt关键词后的括号内常量表达式应是整数常量表达式,用于指定整数类型的宽度。对于unsigned _BitInt类型,整数常量表达式的值域范围为[1, BITINT_MAXWIDTH],例如:unsigned _BitInt(3);对于signed _BitInt类型,整数常量表达式的值域范围为[2, BITINT_MAXWIDTH],例如:signed _BitInt(5)。
对于推断类型的声明,声明说明符序列中的可选元素(如果存在)与推断出的类型有关(对于限定符和属性说明符),或者与声明的对象有关(对于对齐说明符)。
同一项中使用逗号分隔的多集表示相同类型(位字段除外。)。对于位字段,int类型说明符指定的类型是与signed int类型相同,还是与unsigned int类型相同,将由实现定义。
1、结构说明符
结构类型是由一系列结构成员构成的类型,这些成员的存储空间是有序分配的。关键词struct表示指定的类型是结构类型。C语言中结构说明符具有以下两种语法格式:
struct attribute-specifier-sequenceopt identifieropt {member-declaration-list} struct attribute-specifier-sequenceopt identifier
其中struct是C语言关键词;attribute-specifier-sequence是属性说明符序列;identifier是标识符,也是结构标记;member-declaration-list是成员声明列表。
第一种格式声明的结构类型是完整类型,如果缺省标识符(identifier),声明的结构是匿名结构(anonymous structure)。第二种格式声明的结构类型是完整类型还是不完整类型,与其位置有关;如果声明在对应完整类型声明之前,声明类型是不完整类型;如果声明在对应完整类型声明之后,声明类型是完整类型。
struct s1; //这里struct s1是不完整类型。 struct s1{ int i; }; struct s2{ int i; }; struct s2; //这里struct s2是完整类型。
ISO/IEC 9899:2024标准对结构声明作了一些限制,具体如下:
-- 未声明匿名结构或者匿名联合的结构声明应包含成员声明符列表(member-declarator-list)。
struct s{ //合法。 struct { //匿名结构。 int a; double d; }; }; struct s1{ //未定义行为。 };
-- 结构成员类型不能是不完整类型或者函数类型,因此结构不能包含自身类型成员,但可以包含指向自身类型对象的指针。
struct s1{ int i; struct s1 number; //非法。 }; struct s2{ int i; struct s2 *ptr; //合法。 };
-- 如果一个结构类型包含多个命名成员,该结构类型的最后一个成员可以是不完整数组类型(incomplete array type),但此类结构(以及任何包含此类结构成员的联合(可能是递归的。))不能是结构成员或者数组元素。
struct s { //合法。 int i; int arr[]; }; struct s a[5]; //非法。
-- 指定位字段宽度的表达式应是非负值的整数常量表达式,且其值不能超过指定类型的对象宽度;如果整数常量表达式值为0,声明应没有声明符。对于bool类型对象,位数至少是CHAR_BIT位,其宽度(符号位和值位)只有1位。
struct s{ int i : 8; int : 0; };
-- 位字段类型应是bool类型、signed int类型、unsigned int类型、位精确整数类型或者其它实现定义类型的限定版本或者非限定版本。是否允许原子类型将由实现定义。
-- 属性说明符序列不应出现在无成员声明列表的结构说明符中,以下情况除外:
struct attribute-specifier-sequence identifier;
属性说明符序列中的属性(如果存在)视为结构的属性。
struct [[deprecated]] s; //合法,struct s类型具有[[deprecated]]属性。 struct s{ //合法,从上次声明继承了[[deprecated]]属性。 int i; double d; }; int f1(struct s *); //合法,struct s类型具有[[deprecated]]属性。 int f2(struct [[deprecated]] s ss); //非法,不符合结构中属性的语法格式。
如果结构说明符中存在成员声明列表,则在编译单元内声明了一种新结构类型。成员声明列表是结构成员的声明序列,如果成员声明列表没有直接或者通过匿名结构(或者联合)包含任何命名成员,其行为是未定义的。在终止成员声明列表的}符号之前,结构类型是不完整类型,之后结构类型是完整类型。
struct s{ int i; double d; //这里结构类型是不完整的。 }; //这里结构类型是完整的。
结构成员的类型可以是任何完整对象类型(complete object type),但可变修改类型(variably modified type)除外。
int n; struct s{ int i; int (*ptr)[n]; //非法,成员类型是可变修改类型。 };
结构成员类型不能是可变修改类型,因为结构成员名不是普通标识符,结构成员具有独立命名空间。
结构对象中每个非位字段成员以适合其类型的实现定义方式对齐;结构成员地址按其声明顺序递增。指向结构对象的指针经过适当转换后指向结构的初始成员(如果初始成员是位字段,将指向其所在单元。);反之亦然。结构对象内可能存在未命名填充,但结构对象的起始部分不能存在未命名填充,结构对象的末尾可以存在未命名填充。
struct s{ int i1; double d; int i2; } ss; int *ptr = (int *)&ss; //指针ptr指向ss.i1。 printf("ss: %zu\n", sizeof ss); //输出ss: 24。 printf("int type: %zu\n", sizeof(int)); //输出int type: 4。 printf("double type: %zu\n", sizeof(double)); //输出double type: 8。 printf("i1: %p\n", &ss.i1); //输出i1: 0x7ffffcc20。 printf("d : %p\n", &ss.d); //输出d : 0x7ffffcc28。 printf("i2: %p\n", &ss.i2); //输出i2: 0x7ffffcc30。
结构声明中的属性说明符序列适用于声明的结构类型;结构成员声明中的属性说明符序列适用于声明的结构成员。如果不存在结构成员声明符,属性说明符序列不应出现。属性说明符序列只影响其出现的成员声明或者类型名,而不影响同一类型的其它类型或者声明。
struct s{ [[deprecated]] int x, y; //合法。x、y都具有[[deprecated]]属性。 [[deprecated]] int : 0; //非法。不存在成员声明符。 };