5

宣言内の関数の署名が定義と一致しない場合でも、次のコードはどのように機能しますか? 関数宣言には空のパラメーター リストがありますが、定義には 1 つのパラメーターがあります。コンパイラがエラーを出さないのはなぜですか?

#include <stdio.h>
double f(); //function declaration
int main(void)  
{ 
   printf("%f\n", f(100.0)); 
}
double f(double param) //function definition
{
   return 5 * param ; 
}

正常にコンパイルおよび実行されます ( ideone )。

しかし、定義内のパラメーターのdoubleを からに変更するfloatと、次のエラー ( ideone )が発生します。

prog.c:7: エラー: 'f' の型が競合しています<br> prog.c:8: 注: デフォルトの昇格を持つ引数の型は、空のパラメーター名リスト宣言と一致できません
prog.c:2: エラー: 'f' の前の宣言はここにありました

の何が問題になっていfloatますか? floatでエラーが発生するのにエラーが発生するのはなぜdoubleですか?

以下は、宣言と定義のペアのリストと、機能するペアと機能しないペアです。

  • 作品(イデオネ

    double f();              //declaration
    double f(double param);  //definition
    
  • 動作しません (イデオン)

    double f();              //declaration
    double f(float param);   //definition
    
  • 作品(イデオネ

    float f();               //declaration
    float f(double param);   //definition
    
  • 動作しません (イデオン)

    float f();               //declaration
    float f(float param);    //definition
    

パラメータタイプfloat


だから私は基本的に2つの質問があります:

  • 宣言と定義に不一致があるにもかかわらず、最初の例が機能するのはなぜですか?
  • parameter-type が の場合に機能しないのはなぜfloatですか?

セクション 6.5.2.2 (C99) を理解しようとしましたが、言語が非常に不可解で、明確に理解できませんでした。正しいセクションを読んだかどうかさえわかりません。ですから、これらの行動を簡単な言葉で説明してください。

4

4 に答える 4

6

宣言が定義と一致しないというあなたの仮定は正しくありません。(これは C++ では当てはまりますが、C では当てはまりません)。C言語では、

double f();

宣言は関数を完全に宣言していません。つまり、プロトタイプを導入していません。f関数が存在し、その戻り値の型が であるという事実のみを通知しdoubleます。fs 引数の数と型についてはまったく何も述べていません。引数は絶対に何でもかまいません。その意味で、例の宣言は定義と一致します (つまり、C コンパイラにとって十分な定義と矛盾しません)。

引数を取らない関数を本当に宣言したい場合はvoid、パラメーター リストで明示的に指定する必要があります。

double f(void);

それは確かに定義に矛盾します。もともとあるものはありません。

空のパラメーター リストで宣言された関数を呼び出すときは()、適切な型の適切な数の引数を指定する必要があります。間違えた場合の動作は未定義です。これは、実際のパラメーターの型を に変更したときにコンパイラが警告する内容ですfloat

宣言と定義の「ペア」の分析は完全には正しくありません。それは見当違いです。宣言と定義についてではありません。それは本当に定義と関数を呼び出す方法に関するものです。元のケースでは、引数で呼び出しdouble、関数はdoubleパラメーターで宣言されています。だからすべてが一致しています。ただし、引数を指定して呼び出しdouble、パラメーターを指定して宣言するとfloat、不一致が発生します。

floatまた、関数がプロトタイプなしで宣言されている場合、引数は常に引数に昇格されることに注意してdoubleください。このため、引数リストfloatで宣言された関数に引数を渡すことはできません。()引数が必要な場合はfloat、常にプロトタイプを使用してください (引数charshort引数にも同じことが当てはまります)。

于 2011-06-05T16:14:52.857 に答える
4

C では、関数宣言を空にすることができます。C99 6.7.5.3/14 から:

その関数の定義の一部ではない関数宣言子の空のリストは、パラメーターの数または型に関する情報が提供されないことを指定します。

voidこれは、関数に引数がないことを明示的に示しているパラメーター リストとは異なります。6.7.5.3/10 から:

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

型が宣言されていない場合、宣言はプロトタイプではないことにも注意してください。6.2.1/2 から:

関数プロトタイプは、パラメーターの型を宣言する関数の宣言です。

2 番目の質問は、実際には C99 6.5.2.2/6 に関連しています。

呼び出された関数を示す式がプロトタイプを含まない型を持つ場合、各引数に対して整数昇格が実行され、型を持つ引数 floatは に昇格されdoubleます。

したがって、あなたの場合、関数が呼び出されるたびにfloatに昇格し、aがコール スタック (またはその中など) に置かれます。しかしもちろん、関数定義が を受け取る場合、コール スタックからを読み取るため、バイナリの非互換性が発生します。コンパイラはこれを認識しているため、エラーメッセージが表示されます。doubledoubleeaxfloatfloat

于 2011-06-05T16:08:06.863 に答える
3

C では、宣言内の空のパラメーター リストは、関数を 0 個以上の引数で呼び出すことができることを意味します。

このような関数は、浮動小数点数で呼び出されると、暗黙的にdouble. (そして としての積分パラメータint。)

したがって、foo(100.0) を呼び出すときは、double で呼び出しています。float で呼び出そうとすると、呼び出し時に引数が double に変換されます。

double と float の受け渡し方法が異なるため、float を取る関数を定義した場合、これは機能しません。したがって、コンパイラはエラーを表示するのに役立ちます。

1985年ではなく2011年にこの間違いを犯したことを嬉しく思います.コンパイラは以前はかなり愚かで、追跡するのは悪夢のようなバグだったからです.

結論: 最新の C で空のパラメーター リストを使用して関数を宣言するのは非常に悪いスタイルです。関数を適切に宣言し、複数の翻訳単位で参照される場合は、宣言をヘッダー ファイルに入れます。

[編集]

コメントでdetlyが指摘しているように、実際に関数をゼロ引数を取るように宣言したい場合は、それを take と宣言してvoidください。(または C++ に切り替える...)

于 2011-06-05T16:11:00.593 に答える
1

C では、空の関数宣言は...C++ での使用に似ています。つまり、任意の数とタイプの引数に一致します。floata の代わりに aを使用する際の問題doubleは、float自動的に に昇格することdoubleです。(C++ 表記法を借りるために) を呼び出すときf(...)、期待される型がわからないため、double に昇格されます。後で引数fを取るように再宣言すると、 asfloatの暗黙の宣言と競合します。ff(double)

于 2011-06-05T16:11:57.563 に答える