K&R C では (ANSI C の場合はさらに顕著ですが)、同じ要素を参照する 2 つの宣言を別のものとして扱うことができます。ANSI C は、このような「ある程度似ている」型を示すために、「互換型」という用語を使用します。この節では、この互換型と、2 つの互換型を結合した「複合型」を説明します。
C プログラムにおいて各オブジェクトまたは関数の宣言が 1 度しか許されていないのであれば、互換型は必要ないはずです。しかし、同じ要素を参照する複数の宣言を許可するリンク、関数のプロトタイプ、および分割コンパイルには、このような機能が必要です。複数の翻訳単位 (ソースファイル) 間では、型の互換性の規則は 1 つの翻訳単位内のものとは異なります。
各コンパイルでは別々のソースファイルを参照するため、分割コンパイル間の互換型に対して、ほとんどの規則の内容は次のように構造化されています。
一致するスカラー (整数、浮動小数点、およびポインタ) 型は、同じソースファイル内にある場合のように、互換性を持たなければならない。
一致する構造体、共用体、および列挙型のメンバー数は同じでなければならない。一致する各メンバーは (分割コンパイルという意味で) 互換型を持たなければならない (ビットフィールド幅も含む)。
一致する構造体のメンバーの順番は、同じでなければならない。共用体と列挙型のメンバーの順番は問題にならない。
一致する列挙型のメンバーの値は、同じでなければならない。
さらに、構造体、共用体、および列挙型のメンバーの名前 (名前なしメンバーに名前がないということ) も一致しなければなりません。しかし、それぞれのタグは必ずしも一致する必要はありません。
同じスコープ内の 2 つの宣言が同じオブジェクトまたは関数を記述するとき、この 2 つの宣言は互換型を指定しなければなりません。これら 2 つの型は次に、最初の 2 つと互換性を持つ、1 つの複合型に結合されます。複合型については後で説明します。
互換型は再帰的に定義されます。一番下は型指定子のキーワードです。これらの規則は、unsigned short は unsigned short int と同じであり、型指定子なしの型は int を持つ型であることを示します。他のすべての型は、派生元の型が互換性を持つときだけ、互換性を持ちます。たとえば、修飾子 const と volatile が同じであり、未修飾型が互換性を持つ場合、2 つの修飾型は互換性を持ちます。
2 つのポインタ型が互換性を持つためには、この 2 つのポインタが指す型が互換性を持ち、2 つのポインタが同じように修飾されていなければなりません。ポインタの修飾子は * の後に指定されることを念頭に置いて、次の例を見てください。
int *const cpi; int *volatile vpi;
上記 2 つの宣言は、同じ型 int を指すが修飾が異なる 2 つのポインタを宣言しています。
2 つの配列型が互換性を持つためには、この 2 つの配列の要素の型が互換性を持たなければなりません。両方の配列の型のサイズが指定されている場合は、両方のサイズも一致しなければなりません。つまり、不完全な配列型 (「不完全な型」を参照) は、他の不完全な配列型とも、サイズが指定されている配列型とも互換性を持ちます。
関数が互換性を持つためには、次の規則に従わなければなりません。
2 つの関数型が互換性を持つためには、その戻り型が互換性を持たなければなりません。どちらか、あるいは両方の関数型がプロトタイプを持つ場合、規則はより複雑になります。
プロトタイプを持つ 2 つの関数型が互換性を持つためには、(省略記号 (...) も含む) パラメータの数が同じで、対応するパラメータもパラメータ互換でなければなりません。
古い形式の関数定義がプロトタイプを持つ関数型と互換性を持つためには、プロトタイプの最後のパラメータが省略記号 (...) であってはなりません。プロトタイプの各パラメータは、デフォルトの引数拡張の適用後、対応する古い形式のパラメータとパラメータ互換でなければなりません。
古い形式の関数宣言 (定義ではない) が、プロトタイプを持つ関数型と互換性を持つためには、プロトタイプの最後のパラメータが省略記号 (...) であってはなりません。プロトタイプのすべてのパラメータは、デフォルトの引数拡張で影響を受けない型でなければなりません。
2 つの型がパラメータ互換になるためには、これら 2 つの型は、1 番上に修飾子があればそれが削除された後、そして、関数型または配列型が適切なポインタ型に変換された後に、互換性を持たなければなりません。
signed int は int と同じように動作します。ただし、ビットフィールドで通常の int が unsigned 動作を示す数になる場合を除きます。
また、列挙型は同じ整数型と互換性を持たなければなりません。移植可能なプログラムの場合、これは、列挙型が別の型であることを意味します。一般的に、ANSI C 規格はこのように列挙型を扱います。
2 つの互換型から 1 つの複合型への作成も再帰的に定義されます。不完全な配列型や古い形式の関数型を使用することにより、互換型をお互いに異なるようにできます。同様に、複合型の最も簡単に記述するには、元の両方の型 (元の型のすべての使用可能な配列サイズとすべての使用可能なパラメータリストも含む) と型の互換性を持たせればよいでしょう。