わかりました、それで私はこの主題について別の意見を聞いたことがあります。
宣言void f();
とvoid f(void);
はまったく同じことを意味し、関数f
はパラメーターを取りません。定義についても同様。
宣言void f(void);
は、f
がパラメーターを取らないことを意味します。
宣言void f();
は、関数f
にパラメーターがある場合とない場合があることを意味し、パラメーターがある場合、パラメーターの種類、またはパラメーターの数はわかりません。省略記号と同じではないことに注意してください、va_list
は使用できません。
今、ここから物事が面白くなってきます。
宣言:
void f();
定義:
void f(int a, int b, float c)
{
//...
}
宣言:
void f();
定義:
void f()
{
//...
}
正しい引数、間違った引数、引数なしでf
を呼び出した場合、ケース1と2でコンパイル時に何が起こりますか?実行時に何が起こりますか?
f
を引数付きで宣言し、引数なしで定義すると、違いが生じますか?関数本体から引数に対処できるはずですか?
より多くの用語(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
_に加えて、_-Wmissing-prototypes
_および_-Wall -Wextra
_でコンパイルすることをお勧めします。
_void f(); // declaration
void f(int a, int b, float c) { } // ERROR
_
宣言は関数本体と一致しません!これは実際にはcompile timeエラーであり、プロトタイプのない関数ではfloat
引数を使用できないためです。プロトタイプ化されていない関数で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項を参照
一方のタイプにパラメータータイプリストがあり、もう一方のタイプが関数定義の一部ではなく、空の識別子リストを含む関数宣言子によって指定されている場合、パラメーターリストにはEllipsisターミネーターがなく、各パラメーターのタイプはデフォルトの引数プロモーションの適用から生じる型と互換性があります。
正しい引数、間違った引数、引数なしで
f
を呼び出した場合、ケース1と2でコンパイル時に何が起こりますか?実行時に何が起こりますか?
f()
を呼び出すと、デフォルトのプロモーションを使用してパラメーターが昇格されます。昇格された型がf()
の実際のパラメーター型と一致する場合、すべてが適切です。それらが一致しない場合は、おそらくコンパイルされますが、間違いなく未定義の動作になります。
「未定義の動作」は、「何が起こるかについて保証するものではない」ための仕様です。あなたのプログラムがクラッシュするかもしれませんし、うまくいくかもしれませんし、夕食にあなたの義理を招待するかもしれません。
コンパイル時に診断を取得するには2つの方法があります。モジュール間の静的解析機能を備えた洗練されたコンパイラを使用している場合、おそらくエラーメッセージが表示されます。 _-Wstrict-prototypes
_を使用して、GCCでプロトタイプ化されていない関数宣言のメッセージを取得することもできます。これは、すべてのプロジェクトで有効にすることをお勧めします(Gtk 2を使用するファイルを除く)。
f
を引数付きで宣言し、引数なしで定義すると、違いが生じますか?関数本体から引数に対処できるはずですか?
コンパイルすべきではありません。
関数の引数が関数の定義と一致しない場合、実際には2つのケースがあります。
_char *
_を期待する関数に_void *
_を渡したり、その逆を行ったりしてもかまいません。
値が両方の型で表現可能である限り(つまり、負ではなく、範囲外ではない限り)、その型の符号なしバージョンを期待する関数に符号付き整数型を渡すことは問題ありません署名されたタイプ)。
1:char
が_unsigned int
_に昇格するのはpossibleですが、これは非常にまれです。
C99以降を使用している場合、すべてが本当に重要なポイントです(そして、古い組み込みシステムまたはそのようなものにこだわっていない限り、おそらくshouldより新しいものを使用しているでしょう) 。
C99/C11セクション_6.11.6 Future language directions, Function declarators
_状態:
空の括弧を持つ関数宣言子(プロトタイプ形式のパラメーター型宣言子ではない)の使用は、廃止された機能です。
したがって、void f();
などの使用は完全に避けてください。
パラメータを取る場合は、それらをリストして、適切なプロトタイプを作成します。そうでない場合は、void
を使用して、パラメータを取らないことを明確に示します。
C++では、f()とf(void)は同じです
Cでは、これらは異なり、f()関数を呼び出している間に任意の数の引数を渡すことができますが、f(void)に引数を渡すことはできません