5

次のコードを検討してください。

int main (void) {
    int i = xyzzy();
    return i;
}
int xyzzy (void) {
    return 42;
}

現在、プロトタイプxyyzyは使用時に不明ですが、これは c89 モードで機能しますint。これは、プロトタイプを持たない関数のデフォルトの戻り値の型が暗黙の関数プロトタイプと実際の関数に互換性があるためです。

実際、関数の戻り値の型を に変更するとfloat、(予想どおり) 次のようになります。

testprog.c:6: error: conflicting types for 'xyzzy'
testprog.c:2: error: previous implicit declaration of 'xyzzy' was here

暗黙のプロトタイプと実際の関数が一致しなくなるためです。

でコンパイルされた元のコードでgcc --std=c89 --pedantic -Wall -Wextraは、次の警告のみが表示されます。

testprog.c: In function 'main':
testprog.c:2: warning: implicit declaration of function 'xyzzy'

c89 には次のように記載されているため、これは予想されることです3.7.1 Function definitions

extern int max(int a, int b) { ... }:これexternはストレージ クラス指定子でintあり、型指定子です (これらはデフォルトであるため、それぞれを省略できます)。

とで3.3.2.2 Function calls

関数呼び出しで括弧で囲まれた引数リストの前にある式が識別子のみで構成され、この識別子の宣言が表示されない場合、識別子は、関数呼び出しを含む最も内側のブロックでextern宣言とまったく同じように暗黙的に宣言されます。 int識別子(); 現れた。

したがって、関数を宣言する前に関数を使用すると、デフォルトのプロトタイプが確実に作成されます。


ただし、これらのフレーズは両方とも c99 で削除されており、代わりに6.5.2.2 Function calls(私の太字)が見つかります。

呼び出された関数を示す式がオブジェクト型を返す関数へのポインタ型を持つ場合、関数呼び出し式はそのオブジェクト型と同じ型を持ち、6.8.6.4 で指定されているように決定された値を持ちます。それ以外の場合、関数呼び出しの型は void です。

関数を呼び出そうとしたときにビューに宣言がない場合、void戻り値の型で暗黙的に宣言されていることを意味すると理解しています。

それでも、でコンパイルするとgcc --std=c99 --pedantic -Wall -Wextra、暗黙の宣言について同じ警告が表示されます。

c99 は、その関数を暗黙的に戻り値として宣言すべきではありませんでしたvoidか? previous implicit declarationもしそうなら、私はそれを return として再宣言しようとしたときに得たものと同様のエラーを期待していたでしょうfloat.

ここで壊れていgccますか、それとも標準に何か欠けていますか?

4

2 に答える 2

4

あなたは規格を間違って読んでいます。C には暗黙的な関数宣言のようなものはありません。C99 によって言語から削除されます。

GCC は、暗黙的な関数宣言のように見える誤った構造を検出すると、警告を発行します。これは、標準に関する限り問題ありません。標準ではここで診断が必要であり、警告は診断です。-Werror=implicit-function-declarationこれをエラーにするには、GCC に flag を使用できます。

于 2013-02-10T16:44:23.480 に答える
1

これは6.5.1 一次式の注で説明されています。

2 - 識別子は、オブジェクト (この場合は左辺値) または関数 (この場合は関数指示子) を指定するものとして宣言されている場合、一次式です。79)


79) したがって、宣言されていない識別子は構文違反です。

5.1.1.3 Diagnosticsでは、呼び出された関数を表す式として宣言されていない識別子を含む関数呼び出し式の構文違反に応答して診断メッセージを生成するために、準拠する実装が必要です。もちろん、あたかも識別子が暗黙のintC89 スタイルで宣言されているかのように、プログラムのコンパイルに進むことは自由です。

段落 6.5.2.2p5 は、制約 6.5.2.2p1 を参照して読む必要があります。

void1 - 呼び出された関数を示す式は、配列型以外のオブジェクト型を返すか、または返す関数へのポインタ型を持つ必要があります。

したがって、「呼び出された関数を示す式」が「オブジェクト型を返す関数へのポインタ」型を持たない場合、事実上(制約 6.5.2.2p1 により) 「関数を返す関数へのポインタ」型を持たなけれなりません。このケースは、6.5.2.2p5の「それ以外」でカバーされています。つまり、[角括弧]に挿入すると、次のようになります。void

5 - 呼び出された関数を示す式が、オブジェクト型を返す関数への型ポインタを持つ場合、関数呼び出し式はそのオブジェクト型と同じ型を持ち、6.8.6.4 で指定されているように決定された値を持ちます。それ以外の場合[つまり、呼び出された関数を示す式が、 を返す関数へのポインター型であるvoid場合]、関数呼び出しの型はvoidです。

これは、voidオブジェクト型ではなく、特殊な言語が必要な場合です。呼び出された関数式が宣言されていない識別子であること、または宣言されていない識別子を含むことのライセンスではありません。

于 2013-08-05T13:52:03.983 に答える