C の void 引数 void foo(void) を使用する方が良いですか、それとも void foo() を使用しない方が良いですか?

void foo(void);

これは、C で「パラメーターなし」と言う正しい方法であり、C++ でも機能します。

ただし:

void foo();

C と C++ では意味が異なります。 C では「未知の型のパラメータをいくつでも取ることができる」という意味で、C++ では foo(void) と同じ意味です。 .

可変引数リスト関数は本質的にタイプセーフではないため、可能な限り避ける必要があります。


C でパラメータを指定する方法は 2 つあります。1 つは識別子リストを使用する方法で、もう 1 つはパラメータ タイプ リストを使用する方法です。識別子リストは省略できますが、型リストは省略できません。したがって、ある関数が関数定義で引数を取らないと言うには、(省略された) 識別子リストでこれを行います

void f() {
    /* do something ... */
}

そして、これはパラメータ型リストで:

void f(void) {
    /* do something ... */
}

パラメーターの型リストでパラメーターの型が 1 つだけ void の場合 (その場合、名前を持たない必要があります)、それは関数が引数を取らないことを意味します。しかし、関数を定義するこれら 2 つの方法には、宣言する内容に関して違いがあります。

識別子リスト

最初のものは、関数が特定の数の引数を取ることを定義しますが、識別子リストを使用するすべての関数宣言と同様に、カウントも必要なものの型も伝達されません。したがって、呼び出し元は事前にタイプとカウントを正確に知っている必要があります。そのため、呼び出し元が何らかの引数を指定して関数を呼び出した場合、動作は未定義です。たとえば、呼び出された関数が制御を取得したときに別のレイアウトを想定しているため、スタックが破損する可能性があります。

関数パラメーターで識別子リストを使用することは非推奨です。これは昔から使用されており、今でも多くの製品コードに存在しています。それらは、これらの引数の昇格のために重大な危険を引き起こす可能性があり (昇格された引数の型が関数定義のパラメーターの型と一致しない場合、動作も未定義です!)、もちろん安全性ははるかに低くなります。したがって、常に void を使用してください 関数の唯一の宣言と定義の両方で、パラメーターのない関数のこと。

パラメータ タイプ リスト

2 番目のものは、関数がゼロ引数を取り、それを伝達することも定義します - 関数が prototype と呼ばれるパラメーター型リストを使用して宣言されるすべての場合と同様に .呼び出し元が関数を呼び出して何らかの引数を与えると、それはエラーであり、コンパイラは適切なエラーを吐き出します。

関数を宣言する 2 番目の方法には、多くの利点があります。もちろん、パラメーターの量とタイプがチェックされます。もう 1 つの違いは、コンパイラはパラメーターの型を認識しているため、引数の暗黙的な変換をパラメーターの型に適用できることです。パラメーターの型リストが存在しない場合、これは実行できず、引数は昇格された型に変換されます (これは、既定の引数の昇格と呼ばれます)。 char int になります 、たとえば float double になります .

関数の複合型

ちなみに、ファイルに省略された識別子リストとパラメーター型リストの両方が含まれている場合は、パラメーター型リストが「優先」されます。最後の関数の型にはプロトタイプが含まれています:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

それは、両方の宣言が矛盾することを何も言っていないからです。ただし、2番目にはさらに言いたいことがありました。つまり、1 つの引数が受け入れられます。逆に同じことができます

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

1 つ目は識別子リストを使用して関数を定義し、2 つ目はパラメーター型リストを含む宣言を使用してそのプロトタイプを提供します。


void foo(void) 明示的に次のように述べているため、より優れています:パラメータは許可されていません。

void foo() 少なくともこれが関数の定義ではなく宣言である場合、(一部のコンパイラでは) パラメータを送信できることを意味します。