web-dev-qa-db-ja.com

CとC ++のf()とf(void)の違いを一度だけ理解する

わかりました、それで私はこの主題について別の意見を聞いたことがあります。

C++の場合

宣言void f();void f(void);はまったく同じことを意味し、関数fはパラメーターを取りません。定義についても同様。

Cの場合

宣言void f(void);は、fがパラメーターを取らないことを意味します。

宣言void f();は、関数fにパラメーターがある場合とない場合があることを意味し、パラメーターがある場合、パラメーターの種類、またはパラメーターの数はわかりません。省略記号と同じではないことに注意してください、va_listは使用できません。

今、ここから物事が面白くなってきます。

事例1

宣言:

void f();

定義:

void f(int a, int b, float c)
{
   //...
}

事例2

宣言:

void f();

定義:

void f()
{
   //...
}

質問:

正しい引数、間違った引数、引数なしでfを呼び出した場合、ケース1と2でコンパイル時に何が起こりますか?実行時に何が起こりますか?

追加の質問:

fを引数付きで宣言し、引数なしで定義すると、違いが生じますか?関数本体から引数に対処できるはずですか?

42
user500944

より多くの用語(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);
}
_

このプログラムでは、aintに昇格されます1 cdoubleに昇格されます。したがって、f()の定義は次のようにする必要があります。

_void f(int a, int b, double c)
{
    ...
}
_

C99 6.7.6 15項を参照

一方のタイプにパラメータータイプリストがあり、もう一方のタイプが関数定義の一部ではなく、空の識別子リストを含む関数宣言子によって指定されている場合、パラメーターリストにはEllipsisターミネーターがなく、各パラメーターのタイプはデフォルトの引数プロモーションの適用から生じる型と互換性があります。

回答1

正しい引数、間違った引数、引数なしでfを呼び出した場合、ケース1と2でコンパイル時に何が起こりますか?実行時に何が起こりますか?

f()を呼び出すと、デフォルトのプロモーションを使用してパラメーターが昇格されます。昇格された型がf()の実際のパラメーター型と一致する場合、すべてが適切です。それらが一致しない場合は、おそらくコンパイルされますが、間違いなく未定義の動作になります。

「未定義の動作」は、「何が起こるかについて保証するものではない」ための仕様です。あなたのプログラムがクラッシュするかもしれませんし、うまくいくかもしれませんし、夕食にあなたの義理を招待するかもしれません。

コンパイル時に診断を取得するには2つの方法があります。モジュール間の静的解析機能を備えた洗練されたコンパイラを使用している場合、おそらくエラーメッセージが表示されます。 _-Wstrict-prototypes_を使用して、GCCでプロトタイプ化されていない関数宣言のメッセージを取得することもできます。これは、すべてのプロジェクトで有効にすることをお勧めします(Gtk 2を使用するファイルを除く)。

回答2

fを引数付きで宣言し、引数なしで定義すると、違いが生じますか?関数本体から引数に対処できるはずですか?

コンパイルすべきではありません。

例外

関数の引数が関数の定義と一致しない場合、実際には2つのケースがあります。

  1. _char *_を期待する関数に_void *_を渡したり、その逆を行ったりしてもかまいません。

  2. 値が両方の型で表現可能である限り(つまり、負ではなく、範囲外ではない限り)、その型の符号なしバージョンを期待する関数に符号付き整数型を渡すことは問題ありません署名されたタイプ)。

脚注

1charが_unsigned int_に昇格するのはpossibleですが、これは非常にまれです。

53
Dietrich Epp

C99以降を使用している場合、すべてが本当に重要なポイントです(そして、古い組み込みシステムまたはそのようなものにこだわっていない限り、おそらくshouldより新しいものを使用しているでしょう) 。

C99/C11セクション_6.11.6 Future language directions, Function declarators_状態:

空の括弧を持つ関数宣言子(プロトタイプ形式のパラメーター型宣言子ではない)の使用は、廃止された機能です。

したがって、void f();などの使用は完全に避けてください。

パラメータを取る場合は、それらをリストして、適切なプロトタイプを作成します。そうでない場合は、voidを使用して、パラメータを取らないことを明確に示します。

6
paxdiablo

C++では、f()とf(void)は同じです

Cでは、これらは異なり、f()関数を呼び出している間に任意の数の引数を渡すことができますが、f(void)に引数を渡すことはできません

3
rs2012