あなたが尋ねている質問は、実際には 1 つではなく 2 つの質問です。これまでのほとんどの回答は、「これは K&R スタイルです」という一般的なブランケットの回答で全体をカバーしようとしましたが、実際には、K&R スタイルとして知られているものと関係があるのはごく一部にすぎません (C 言語全体を見ない限り)。ある意味で「K&Rスタイル」として:)
最初の部分は、関数定義で使用される奇妙な構文です
int func(p, p2)
void *p;
int p2; /* <- optional in C89/90, but not in C99 */
{
return 0;
}
これは実際には K&R スタイルの関数定義です。他の答えはこれをかなりうまくカバーしています。そして、実際にはそれほど多くはありません。この構文は非推奨ですが、C99 でも完全にサポートされています (C99 の "暗黙的な int なし" ルールを除いて、つまり、C99 では の宣言を省略できませんp2
)。
2 番目の部分は、K&R スタイルとはほとんど関係ありません。関数は「交換された」引数で呼び出すことができるという事実に言及します。つまり、そのような呼び出しではパラメーターの型チェックは行われません。これは、K&R スタイルの定義自体とはほとんど関係ありませんが、プロトタイプを持たない関数とはすべて関係があります。ほら、Cでこのような関数を宣言すると
int foo();
実際には、不明な型の不特定数のパラメータをfoo
取る関数を宣言しています。次のように呼び出すことができます
foo(2, 3);
そして
j = foo(p, -3, "hello world");
ansなど(アイデアはわかります)。
適切な引数を指定した呼び出しのみが「機能」しますが (つまり、他の引数は未定義の動作を生成します)、その正確性を保証するのは完全にあなた次第です。コンパイラは、何らかの方法で正しいパラメーターの型とその総数を魔法のように知っていても、間違ったものを診断する必要はありません。
実は、この振る舞いはC 言語の特徴です。危険なものですが、それでも機能です。それはあなたがこのようなことをすることを可能にします
void foo(int i);
void bar(char *a, double b);
void baz(void);
int main()
{
void (*fn[])() = { foo, bar, baz };
fn[0](5);
fn[1]("abc", 1.0);
fn[2]();
}
つまり、型キャストなしで「多態性」配列に異なる関数型を混在させます (ただし、可変関数型はここでは使用できません)。繰り返しますが、この手法に固有の危険性は非常に明白です (私はこれを使用したことを覚えていませんが、どこで役立つかは想像できます) が、結局は C です。
最後に、回答の 2 番目の部分を最初の部分にリンクするビットです。K&R スタイルの関数定義を作成すると、関数のプロトタイプが導入されません。関数型に関する限り、func
定義は次のように宣言func
します
int func();
つまり、型もパラメータの総数も宣言されていません。元の投稿では、「...使用するパラメーターの数を指定しているようです...」と言っています。正式にはそうではありません!2 パラメーターの K&R スタイルの定義の後でも、次のようにfunc
呼び出すことができます。func
func(1, 2, 3, 4, "Hi!");
制約違反はありません。(通常、高品質のコンパイラは警告を表示します)。
また、見過ごされがちな事実として、
int f()
{
return 0;
}
も、プロトタイプを導入しない K&R スタイルの関数定義です。「モダン」にするにvoid
は、パラメーターリストに明示的に配置する必要があります
int f(void)
{
return 0;
}
最後に、一般的な考えに反して、K&R スタイルの関数定義とプロトタイプ化されていない関数宣言の両方が C99 で完全にサポートされています。私の記憶が正しければ、前者は C89/90 以降非推奨になっています。C99 では、最初に使用する前に関数を宣言する必要がありますが、宣言はプロトタイプである必要はありません。この混乱は、一般的な用語の混同に起因しているようです。多くの人は関数宣言を「プロトタイプ」と呼んでいますが、実際には「関数宣言」は「プロトタイプ」と同じものではありません。