C99标准 6.2.5 类型
存储在对象中或由函数返回的值的含义取决于用于访问它的表达式的类型。(声明为对象的标识符是这类表达式中最简单的;类型在标识符的声明中指定。)类型被划分为对象类型(充分描述对象的类型)、函数类型(描述函数的类型)和不完整类型(描述对象但缺少确定其大小所需信息的类型)。
声明为_Bool
类型的对象足够大,可以存储值0和1。
声明为char
类型的对象足够大,可以存储基本执行字符集的任何成员。如果基本执行字符集的成员存储在char
对象中,则保证其值为正。如果任何其他字符存储在char
对象中,结果值是实现定义的,但应在该类型可以表示的值的范围内。
有五种标准有符号整数类型,分别是signed char
、short int
、int
、long int
和long long int
。(如6.7.2所述,这些类型和其他类型可以以其他几种方式指定。)也可能有实现定义的扩展有符号整数类型[1]。标准的和扩展的有符号整数类型统称为有符号整数类型[2]。
[1] 实现定义的关键字应具有标识符的形式,可用于7.1.3中所述的任何用途。
[2] 因此,本标准中关于有符号整数类型的任何语句也适用于扩展有符号整数类型。
声明为signed char
类型的对象所占用的存储量与“普通”char
对象一样。“普通”int
对象具有执行环境体系结构建议的自然大小(大到足以包含在头文件<limits.h>
定义的INT_MIN
到INT_MAX
范围内的任何值)。
对于每种有符号整数类型,都有一个对应的(但不同的)无符号整数类型(用关键字unsigned
指定),该类型使用相同的存储量(包括符号信息),并且具有相同的对齐要求。_Bool
类型和与标准有符号整数类型对应的无符号整数类型称为标准无符号整数类型。与扩展有符号整数类型对应的无符号整数类型称为扩展无符号整数类型。标准无符号整数类型和扩展无符号整数类型统称为无符号整数类型[3]。
[3] 因此,本标准中关于无符号整数类型的任何语句也适用于扩展无符号整数类型。
标准有符号整数类型和标准无符号整数类型统称为标准整数类型,扩展有符号整数类型和扩展无符号整数类型统称为扩展整数类型。
对于具有相同符号和不同整数转换秩(见6.3.1.1)的任意两种整数类型,整数转换秩较小的类型的值范围是另一类型值的子范围。
有符号整数类型的非负值的范围是对应的无符号整数类型的子范围,并且在每种类型中相同的值的表示是相同的[4]。涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果将以比结果类型可以表示的最大值大 1 的数的模减。
[4] 相同的表示和对齐要求意味着隐含作为函数的参数、函数的返回值和联合成员的互换性。
有三种实数浮点类型,分别为float
、double
和long double
[5]。
float
类型的值集是double
类型的值集的子集;double
类型的值集是long double
类型值集的子集。
[5] 见“未来语言方向”(6.11.1)。
有三种复数类型,分别为float _Complex
、double _Complex
和long double _Complex
[6],实数浮点和复数类型统称为浮点类型。
[6] 虚部类型的规范见附件G。
对于每个浮点类型都有一个对应的实数类型,它总是一个实数浮点类型。对于实数浮点类型,它是相同的类型。对于复数类型,它是通过从类型名称中删除关键字_Complex
而给出的类型。
每个复数类型作为一种数组类型,它包含的两个相应实数类型的元素有相同的表示和对齐要求;第一个元素等于复数的实部,第二个元素等于复数的虚部。
类型char
、有符号和无符号整数类型以及浮点类型统称为基本类型。即使实现定义了具有相同表示的两个或更多基本类型,它们仍然是不同的类型[7]。
[7] 实现可以定义新的关键字,这些关键字提供了指定基本(或任何其他)类型的替代方法;这并不违反所有基本类型都不同的要求。实现定义的关键字应具有为 7.1.3 所述保留任何用途的标识符的形式。
char
、signed char
和unsigned char
三种类型统称为字符类型。实现应该将char
定义为与signed char
或unsigned char
具有相同的范围、表示和行为[8]。
[8] 在<limits.h>
中定义的CHAR_MIN
将有一个值0或SCHAR_MIN
,这可以用来区分两个选项【译者注:char可能是有符号的,也可能是无符号的】。不管选择什么类型,char
是一种独立于其他两种类型的类型,与两者都不兼容。
枚举由一组命名的整型常数值组成。每个不同的枚举构成不同的枚举类型。
类型char
、有符号和无符号整数类型以及枚举类型统称为整数类型。整数和实数浮点类型统称为实数类型。
整数和浮点类型统称为算术类型。每个算术类型属于一个类型域:实数类型域由实数类型组成,复数类型域由复数类型组成。
void
类型由值的空集组成;它是一个无法完成的不完整类型。
可以从对象、函数和不完整类型构造任意数量的派生类型,如下所示:
- 数组类型描述具有特定成员对象类型(称为元素类型)的连续分配的非空对象集[9]。数组类型的特征是元素类型和数组中的元素数量。数组类型是由它的元素类型派生出来的,如果它的元素类型是T,数组类型有时被称为“T数组”。从元素类型构造数组类型称为“数组类型派生”。
[9] 由于对象类型不包括不完整类型,所以不能构造不完整类型的数组。
-
结构类型描述按顺序分配的非空成员对象集(在某些情况下,是一个不完整的数组),每个数组都有一个可选指定的名称和可能不同的类型。
-
联合类型描述一个重叠的非空成员对象集,每个成员对象都有一个可选指定的名称和可能不同的类型。
-
函数类型描述具有指定返回类型的函数。函数类型的特征是它的返回类型及其参数的数量和类型。一个函数类型被认为是从它的返回类型派生出来的,如果它的返回类型是T,这个函数类型有时被称为“函数返回T”。从返回类型构造函数类型称为“函数类型派生”。
-
指针类型可以从函数类型、对象类型或不完整类型派生,它们称为引用类型。指针类型描述了一个对象,该对象的值提供了对所引用类型实体的引用。从引用类型T派生出来的指针类型有时被称为“指向T的指针”。从引用类型构造指针类型称为“指针类型派生”。
这些构造派生类型的方法可以递归地应用。
算术类型和指针类型统称为标量类型。数组类型和结构类型统称为聚合类型[10]。
[10] 请注意,聚合类型不包括联合类型,因为具有联合类型的对象一次只能包含一个成员。
大小未知的数组类型是不完整类型。对于这种类型的标识符,通过在以后的声明中指定大小(带有内部或外部链接),它就完成了。未知内容的结构或联合类型(如6.7.2.3所述)是不完整类型。对于该类型的所有声明,通过在同一作用域中声明相同的结构或联合标签及其定义内容来完成。
数组、函数和指针类型统称为派生声明符类型。类型T的声明符类型派生是通过对T应用数组类型、函数类型或指针类型的派生,从T构造派生声明符类型。
类型的特征在于它的类型类别,它是派生类型的最外层派生(如上所述,在构造派生类型时),或者是类型本身,如果类型不包含派生类型。
到目前为止所提到的任何类型都是非限定类型。每个非限定类型都有其类型的几个限定版本[11],对应于const
、volatile
和restrict
限定符的一个、两个或全部三个限定符的组合。类型的限定或不限定版本是属于同一类型类别的不同类型,具有相同的表示和对齐要求[12]。派生类型不受其派生的类型的限定符(如果有的话)的限定。
[11] 关于限定数组和函数类型,请参见6.7.3。
[12] 相同的表示和对齐要求意味着隐含作为函数的参数、函数的返回值和联合成员的互换性。
指向void
的指针应具有与指向字符类型的指针相同的表示和对齐要求[12]。类似地,指向兼容类型的限定或不限定版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针应具有相同的表示和对齐要求。所有指向联合类型的指针都应该有相同的表示和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。
示例1:指定为“float *
”的类型具有“float
指针”类型。它的类型类别是指针,而不是浮点类型。该类型的const
限定版本被指定为“float * const
”,而指定为“const float *
”的类型不是限定类型——它的类型是“const float
指针”,是一个限定类型的指针。
示例2:指定为“struct tag (*[5])(float)
”的类型具有“指向返回struct tag
的函数的指针数组”类型。数组的长度为5,函数只有一个float类型的参数。它的类型类别是数组。
提前引用: 兼容类型和复合类型(6.2.7),声明(6.7)。
【译者总结】