その他の用語 (C++ ではなく C): 関数のプロトタイプは、その引数の型を宣言します。それ以外の場合、関数にはプロトタイプがありません。
void f(); // Declaration, but not a prototype
void f(void); // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype
プロトタイプではない宣言は、K&R C の時代からの ANSI C 以前の名残りです。古いスタイルの宣言を使用する唯一の理由は、古いコードとのバイナリ互換性を維持することです。たとえば、GTK 2 には、プロトタイプのない関数宣言があります。これは偶然にもありますが、バイナリを壊さずに削除することはできません。C99 標準コメント:
6.11.6 関数宣言子
括弧が空の関数宣言子 (プロトタイプ形式のパラメーター型宣言子ではない) の使用は廃止予定の機能です。
推奨: GCC/Clang のすべての C コードを、通常の に加えて-Wstrict-prototypes
andでコンパイルすることをお勧めします。-Wmissing-prototypes
-Wall -Wextra
何が起こるのですか
void f(); // declaration
void f(int a, int b, float c) { } // ERROR
宣言が関数本体と一致しません! これは実際にはコンパイル時float
エラーであり、プロトタイプのない関数では引数を持てないためです。プロトタイプ化されていない関数でa を使用できない理由はfloat
、そのような関数を呼び出すと、特定のデフォルト プロモーションを使用してすべての引数が昇格されるためです。固定例を次に示します。
void f();
void g()
{
char a;
int b;
float c;
f(a, b, c);
}
このプログラムでa
は、 はint
1に昇進し、c
に昇格しdouble
ます。したがって、 の定義f()
は次のようにする必要があります。
void f(int a, int b, double c)
{
...
}
C99 6.7.6 パラグラフ 15 を参照してください。
一方の型にパラメーター型リストがあり、もう一方の型が、関数定義の一部ではなく、空の識別子リストを含む関数宣言子によって指定されている場合、パラメーター リストには省略記号終端記号がなく、各パラメーターの型はデフォルトの引数昇格を適用した結果の型と互換性があります。
答え 1
f
ケース 1 とケース 2で、正しい引数、間違った引数、および引数なしで呼び出した場合、コンパイル時に何が起こるでしょうか? 実行時に何が起こるか?
を呼び出すf()
と、パラメータはデフォルトの昇格を使用して昇格されます。昇格した型が の実際のパラメーターの型と一致する場合、f()
すべて問題ありません。それらが一致しない場合、おそらくコンパイルされますが、未定義の動作が発生することは間違いありません。
「未定義の動作」とは、「何が起こるかについて保証しない」という仕様上の言葉です。プログラムがクラッシュするかもしれませんし、問題なく動作するかもしれませんし、義理の家族を夕食に招待するかもしれません。
コンパイル時に診断を取得するには、2 つの方法があります。モジュール間の静的解析機能を備えた高度なコンパイラを使用している場合は、おそらくエラー メッセージが表示されます。-- を使用して、GCC でプロトタイプ化されていない関数宣言のメッセージを取得することもできます-Wstrict-prototypes
(GTK 2 を使用するファイルを除く)。
答え 2
引数付きで宣言f
し、引数なしで定義すると、違いはありますか? 関数本体から引数をアドレス指定できるようにする必要がありますか?
コンパイルされるべきではありません。
例外
実際には、関数の引数が関数定義と一致しないことが許されるケースが 2 つあります。
char *
を期待する関数に渡すことは問題ありませんvoid *
。
値が両方の型で表現可能である限り (つまり、負ではなく、サインタイプ)。
脚注
1 : が に昇格する可能性はありますが、これは非常にまれです。char
unsigned int