对于 K&R C,引用同一实体的两个声明可能是不同的;对于 ISO C 更是如此。ISO C 中使用的术语“兼容类型”表示“足够接近”的类型。本节介绍了兼容类型和“复合类型”,后者是合并两种兼容类型而产生的结果。
如果只允许 C 程序声明每个对象或函数一次,则不一定需要兼容类型。链接(允许两个或更多声明引用相同实体)、函数原型和分别编译全部需要此功能。独立转换单元(源文件)具有与单个转换单元不同的类型兼容性规则。
由于每个编译可能查看不同的源文件,因此独立编译中的大多数兼容类型规则实质上是结构化的:
匹配标量(整型、浮点和指针)类型必须兼容,如同它们在相同的源文件中一样。
匹配结构、联合和枚举必须具有相同数目的成员。每个匹配成员都必须具有兼容类型(从单独编译的意义上讲),包括位字段宽度。
匹配结构必须具有相同顺序的成员。联合和枚举成员的顺序并不重要。
匹配枚举成员必须具有相同的值。
附加要求是,对于结构、联合和枚举,成员的名称(包括缺少未命名成员的名称)必须匹配,但是它们各自的标记不必匹配。
当相同作用域内的两个声明描述相同的对象或函数时,这两个声明必须指定兼容类型。然后这两种类型合并为与这两种类型兼容的单个复合类型。
兼容类型是递归定义的。底部为类型说明符关键字。这些规则规定,unsigned short 与 unsigned short int 相同,不带类型说明符的类型与带有 int 的类型相同。所有其他类型仅当派生它们的类型兼容时才为兼容类型。例如,如果限定符 const 和 volatile 是相同的,且未限定基类型是兼容的,则两个限定类型是兼容的。
要使两种指针类型兼容,它们指向的类型必须兼容,并且必须对这两个指针进行相同的限定。考虑到指针的限定符在 * 之后指定,因此以下两个声明所声明的两个不同限定指针指向同一类型 int。
int *const cpi; int *volatile vpi;
要使两个数组类型兼容,它们的元素类型必须兼容。如果两个数组类型具有指定的大小,则它们必须匹配,即,不完全数组类型(请参见不完全类型)同时与另一不完全数组类型和一个具有指定大小的数组类型兼容。
要使函数兼容,请遵守以下规则:
要使两个函数类型兼容,它们的返回类型必须兼容。如果任一或两个函数类型都有原型,则规则将更加复杂。
为了使具有原型的两个函数类型兼容,它们还必须具有相同数目的参数,包括省略号 (…) 表示法的使用,而且对应参数必须是参数兼容的。
为了使旧式函数定义与具有原型的函数类型兼容,原型参数不得以省略号 (…) 结尾。应用缺省参数提升后,每个原型参数与对应的旧式参数必须是参数兼容的。
为了使旧式函数声明(而不是定义)与具有原型的函数类型兼容,原型参数不得以省略号 (…) 结尾。所有原型参数的类型必须不受缺省参数提升的影响。
为了使两种类型是参数兼容的,在删除顶级限定符(如果有)后,以及将函数或数组类型转换为相应的指针类型后,这两种类型必须是兼容的。
signed int 的行为与 int 相同,不同之处可能在于位字段,其中无格式 int 可能表示无符号的值。
请注意,每个枚举类型必须与某些整数类型兼容。对于可移植的程序,这意味着枚举类型是独立类型。通常,ISO C 标准将枚举类型视为独立类型。
由两个兼容类型构成的复合类型也是递归定义的。兼容类型可能彼此不同的原因在于不完全数组或旧式函数类型。因此,复合类型最简单的描述是,它是与两个原始类型均兼容的类型,包括原始类型中的各个可用数组大小和各个可用参数列表。