6.2.7 兼容类型和复合类型
NOTE
这一节直接关系到“同一个名字在不同声明里写得不完全一样,是否还算同一种类型”。它对头文件、前向声明、函数声明和链接阶段都很关键。
如果两个类型相同,那么它们就是兼容类型。关于两个类型是否兼容的补充规则,在 6.7.3(类型说明符)、6.7.4(类型限定符)和 6.7.7(声明符)中进一步说明。45)此外,用同一标记声明的两个完全结构体类型、联合体类型或枚举类型,如果其成员满足以下要求,则它们也是兼容的:
- 成员之间存在一一对应关系,并且每对对应成员都以兼容类型声明;
- 如果对应成员中的一个带有对齐说明符,则另一个也应带有等价的对齐说明符;
- 如果对应成员中的一个有名字,则另一个也必须具有相同名字。
对于两个结构体,其对应成员必须按相同顺序声明。对于在同一翻译单元中声明的两个联合体,其对应成员也必须按相同顺序声明。对于两个结构体或联合体,其对应位字段必须具有相同宽度。对于两个枚举,其对应成员必须具有相同值;若其中一个具有固定底层类型,则另一个也必须具有兼容的固定底层类型。为了判断类型兼容性,匿名结构体和匿名联合体被视为包含它们的结构体或联合体类型中的普通成员;如果两个匿名结构体或匿名联合体的成员满足上述要求,则它们彼此兼容。
此外,在不同翻译单元中声明的两个结构体、联合体或枚举类型,在以下情况下也是兼容的:
- 两者都没有标记,并且满足上述要求;
- 两者具有相同标记,并且都在各自翻译单元中的某处被补全,且满足上述要求;
- 两者具有相同标记,并且其中至少一个在其翻译单元中未被补全。
除此之外,结构体、联合体或枚举类型都是不兼容的。46)
所有引用同一个对象或函数的声明,都应具有兼容类型;否则行为未定义。
可以从两个兼容类型构造出一个复合类型。如果这两个类型本来就是同一类型,那么复合类型就是该类型本身;否则,复合类型是一个同时与这两个类型兼容、并满足下列条件的类型:
- 如果两者都是结构体类型,或两者都是联合体类型,那么复合类型通过递归地为它们的对应成员形成复合类型来确定。
- 如果两者都是数组类型,则适用以下规则:
- 如果其中一个是已知常量大小数组,则复合类型是该大小的数组。
- 否则,如果其中一个是大小由“未求值表达式”指定的变长数组,则行为未定义。
- 否则,如果其中一个是已指定大小的变长数组,则复合类型是该大小的变长数组。
- 否则,如果其中一个是大小未指定的变长数组,则复合类型是大小未指定的变长数组。
- 否则,两者都是未知大小数组,则复合类型是未知大小数组。
- 其元素类型,是两个元素类型的复合类型。
- 如果两者都是函数类型,则复合参数类型列表中每个参数的类型,都是对应参数类型的复合类型。
- 如果其中一个类型具有标准属性,则复合类型也具有该属性。
- 如果两者都是枚举类型,则复合类型也是枚举类型。
- 如果其中一个是枚举类型,而另一个是“非枚举的整数类型”,则复合类型是否仍是枚举类型,由实现定义。
上述规则会递归应用到构成这两个类型的各层派生类型之上。
如果原始类型中的某一个本身就已经满足复合类型的全部要求,那么复合类型究竟是这个原始类型本身,还是另一个满足这些要求的不同类型,则未作规定。47)
对于一个具有内部链接或外部链接的标识符,如果在某个作用域中对它作出声明,并且该作用域内先前已有对该标识符可见的声明,48)那么若先前声明规定了内部链接或外部链接,则该标识符在后续声明中的类型会变为复合类型。
示例:给定下面两个文件作用域声明:
int f(int (*)(char *), double (*)[3]);
int f(int (*)(char *), double (*)[]);2
所得的函数复合类型为:
int f(int (*)(char *), double (*)[3]);脚注说明
45)两个类型不必完全一致,也可能兼容。
46)没有标记的结构体、联合体、枚举类型,或者未完成的结构体、联合体、枚举类型,在同一翻译单元中都不与任何其他结构体、联合体或枚举类型兼容。
47)“是否为同一类型”这一点,会影响同一作用域中 typedef 名和标记的重新声明。
48)如 6.2.1 所述,后面的声明可以隐藏前面的声明。
前向引用:数组声明符(6.7.7.3)。