関数プロトタイプ宣言と古い形式の関数定義がともに機能するためには、両方が機能的に同じインタフェースを指定しなければなりません。つまり、ANSI C の用語を使用する「互換形式」を持っていなければなりません。
可変引数を持つ関数の場合は、ANSI C の省略記号と古い形式の varargs() 関数定義を併用することはできません。固定数のパラメータを持つ関数の場合、以前の実装で渡したとおりのパラメータの型を指定するだけです。
K&R C では、各引数は、呼び出された関数に渡される直前に、デフォルトの引数拡張に従って変換されました。このような拡張では、int より狭いすべての整数型を int サイズに拡張し、また、任意の float 引数を double に拡張するように指定されていたため、コンパイラとライブラリの両方が単純化されていました。関数プロトタイプを使用すると、よりわかりやすく表現できます。つまり、指定したパラメータの型が、そのまま、関数に渡されるパラメータの型となります。
したがって、既存の (古い形式の) 関数定義用に関数プロトタイプを書く場合、関数プロトタイプに次の型のパラメータは使用できません。
表 E-1 関数プロトタイプに使用できないパラメータchar | signed char | unsigned char | float |
short | signed short | unsigned short |
プロトタイプを書く際には、さらに 2 つの問題があります。typedef 名と、狭い unsigned 型の拡張規則です。
古い形式の関数内のパラメータが typedef 名で宣言されている場合 (off_t や ino_t など)、typedef 名がデフォルトの引数拡張によって影響を受ける型を指しているかどうかを確認することが重要です。上記 2 つの typedef 名 を例にすると、off_t は long です。したがって、関数プロトタイプで使用することは適切な使用方法です。しかし、ino_t は unsigned short であったため、プロトタイプで使用すると、古い形式の定義とプロトタイプが異なる互換性のないインタフェースを指定するため、診断メッセージが発行されます。
最後の問題は、unsigned short の代わりに何を使用するかです。K&R C と ANSI C コンパイラ間の最大の非互換性の 1 つは、unsigned char と unsigned short を int 値に広げるための拡張規則です (「拡張: 符号なし保存と値の保持」を参照)。このような古い形式のパラメータにあたる型は、コンパイル時に使用するコンパイルモードによって異なります。
-Xs と -Xt では unsigned int を使用するべきです。
-Xa と -Xc では int を使用するべきです。
最良の方法は、int または unsigned int のどちらかを指定するように古い形式の定義を変更して、一致する型を関数プロトタイプで使用することです。必要であれば、関数を入力した後でも、より狭い型の値を局所変数に代入できます。
前処理によって影響を受ける可能性のあるプロトタイプでは、ID の使用に気をつけてください。次の例を考えてください。
#define status 23 void my_exit(int status); /* 通常、スコープはプロトタイプで始まり、*/ /* プロトタイプで終わる */
関数プロトタイプは、狭い型を持つ古い形式の関数定義と併用できません。
void foo(unsigned char, unsigned short); void foo(i, j) unsigned char i; unsigned short j; {...}
__STDC__ を適切に使用すれば、古いコンパイラと新しいコンパイラの両方で使用できるヘッダーファイルを作成できます。
header.h: struct s { /* . . . */ }; #ifdef __STDC__ void errmsg(int, ...); struct s *f(const char *); int g(void); #else void errmsg(); struct s *f(); int g(); #endif
次の関数はプロトタイプを使用していますが、古いシステムでもコンパイルできます。
struct s * #ifdef __STDC__ f(const char *p) #else f(p) char *p; #endif { /* . . . */ }
次の例は、更新したソースファイルです (選択肢 3 を使用したもの)。局所関数は古い形式の定義を使用していますが、新しいコンパイラ用にプロトタイプも含まれています。
source.c: #include header.h typedef /* . . . */ MyType; #ifdef __STDC__ static void del(MyType *); /* . . . */ #endif static void del(p) MyType *p; { /* . . . */ } /* . . . */