6

次のコードの理由を知りたいのですが。

void foo(void);
void foo()
{
}

gccで有効です。Cでは、オーバーロードのようなものはなく、上記の宣言(実際には、そのうちの1つは定義です)は2つの異なる関数を宣言します(最初の関数は引数を取りません、2番目の宣言は任意の数の引数を取ることができます)タイプ)。

ただし、最初の関数の定義を提供する場合:

void foo(void)
{
}
void foo()
{
}

今回は再定義のためにコンパイルが失敗します。しかし、それでも、最初のコードは正しく、次のように混乱する可能性があります。

void foo(void);

int main(void)
{
    foo();      //OK
    //foo(5);   //Wrong, despite ->the definition<- allows it
}

void foo()
{
}

一方、このようなものはすぐには無効です。

void foo(int);
void foo() //error: number of arguments doesn't match prototype
{
}

私の最初の前述のコードと比較すると、コンパイラの動作はちょっと奇妙だと思います。intは等しくなく(/*empty list*/)、どちらも等しくありませんvoid

誰かがこれを説明できますか?

4

3 に答える 3

12

関数宣言子に関する標準の最新ドラフトを引用します。

(6.7.6.3/10)リスト内の唯一の項目としてのvoid型の名前のないパラメーターの特殊なケースは、関数にパラメーターがないことを指定します。

(6.7.6.3 / 14)識別子リストは、関数のパラメータの識別子のみを宣言します。その関数の定義の一部である関数宣言子の空のリストは、関数にパラメーターがないことを指定します。

したがって、宣言と定義の宣言子は実際には互換性があり、したがって同じ関数を参照します(もちろん、Cにはそのようなものは存在しないため、オーバーロードは発生しません)。

于 2012-08-18T22:57:38.613 に答える
1

以下の行は関数宣言であり、関数のシグネチャ(foo戻り値のタイプと引数のタイプ)を示しています。

void foo(void);

以下に関数定義があり、関数が何をするかを示しています。何もオーバーロードしません。定義と宣言は署名で一致する必要があります。voidデータ型では、関数定義で省略できます。

void foo()
{
}

はインスタンス化可能な型ではないためvoid(typeの値を持つことはできませんvoid)、関数の定義のシグニチャの引数を省略してもかまいません。ただし、実行しようとすると、次のようになります。

void foo(void*);
void foo() {
}

foo次に、do n't-worry-about-type値へのポインターを取得することが期待されるため、コンパイルエラーが発生します。

于 2012-08-18T22:47:30.877 に答える
0

C標準では、voidを次のように定義しています。-

voidタイプは、空の値のセットで構成されます。完成できない不完全なタイプです。

そして定義

void foo()
{
}

パラメータが定義に有効な空の値のセットであることを意味するため、gccで許可されます。

また、関数宣言のプロトタイプは以下を指定します:-

リスト内の唯一の項目としてのvoid型の名前のないパラメーターの特殊なケースは、関数にパラメーターがないことを指定します。

于 2012-08-18T22:50:17.630 に答える