38

設定

Cで関数を呼び出すときのデフォルトの引数の昇格についていくつか質問があります.C99標準(pdf)のセクション6.5.2.2「関数呼び出し」段落6、7、および8です(強調を追加し、簡単にするためにリストに分割します読む):

パラグラフ 6

  1. 呼び出された関数を示す式がプロトタイプを含まない型を持つ場合、各引数に対して整数昇格が実行され、型を持つ引数floatは に昇格されdoubleます。これらはデフォルト引数のプロモーションと呼ばれます。
  2. 引数の数がパラメーターの数と等しくない場合、動作は未定義です。
  3. 関数がプロトタイプを含む型で定義されていて、プロトタイプが省略記号 ( , ...) で終わっているか、昇格後の引数の型がパラメーターの型と互換性がない場合、動作は未定義です。
  4. 関数がプロトタイプを含まない型で定義されており、昇格後の引数の型が昇格後のパラメーターの型と互換性がない場合、次の場合を除き、動作は未定義です。
    • 一方の昇格型は符号付き整数型で、もう一方の昇格型は対応する符号なし整数型であり、値は両方の型で表現可能です。
    • 両方の型は、文字型またはの修飾または非修飾バージョンへのポインターvoidです。

パラグラフ 7

  1. 呼び出された関数を示す式が、プロトタイプを含む型を持っている場合、引数は、割り当てによるかのように、対応するパラメーターの型に暗黙的に変換され、各パラメーターの型が宣言された型の非修飾バージョンになります。タイプ。
  2. 関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数昇格は、末尾の引数に対して実行されます。

パラグラフ 8

  1. その他の変換は暗黙的に実行されません。特に、引数の数と型は、関数プロトタイプ宣言子を含まない関数定義のパラメーターの数と型と比較されません。

私が知っていること

  • デフォルトの引数の昇格charand shortto int/ unsigned intand floattoですdouble
  • 可変引数関数 ( などprintf) のオプションの引数は、デフォルトの引数昇格の対象です。

記録として、関数プロトタイプに関する私の理解は次のとおりです。

void func(int a, char b, float c);  // Function prototype
void func(int a, char b, float c) { /* ... */ }  // Function definition

質問

私はこれらすべてを理解するのに本当に苦労しています。ここに私が持っているいくつかの質問があります:

  • プロトタイプ化された関数とプロトタイプ化されていない関数の動作は、デフォルトの昇格や暗黙の変換などに関して、実際にそれほど異なるのでしょうか?
  • デフォルト引数の昇格はいつ行われますか? いつもですか?それとも、特別な場合 (可変個引数関数の場合など) だけですか? 関数がプロトタイプ化されているかどうかに依存しますか?
4

3 に答える 3

40

賛成のAProgrammerの答え—これらは本物の商品です。

なぜこのようになっているのか疑問に思っている人のために:1988年以前の暗黒時代には、古典的な「K&R」Cには関数プロトタイプのようなものはなく、デフォルトの引数プロモーションが開始されました。 「無料」。レジスタにバイトを入れるのに、レジスタにワードを入れるよりもコストがかからないため、(b)パラメータの受け渡しで発生する可能性のあるエラーを削減できます。その2番目の理由は、それを完全に削減することはありませんでした。そのため、ANSI Cでの関数プロトタイプの導入は、C言語でこれまでで最も重要な変更でした。

デフォルトのプロモーションが開始される時期について:デフォルトの引数プロモーションは、引数の予想されるタイプが不明な場合、つまり、プロトタイプがない場合、または引数が可変個引数である場合に正確に使用されます。

于 2009-08-10T17:23:50.643 に答える
36
  • (可変個引数ではない)プロトタイプを持つ関数のパラメーターは、対応するタイプに変換されます。これは、char、short、floatのいずれかになります。

  • プロトタイプおよび可変個引数パラメーターのない関数へのパラメーターは、デフォルトの引数昇格の対象となります。

プロトタイプを使用して関数を定義し、プロトタイプなしで使用する場合、またはその逆の場合、char型、short型、またはfloat型のパラメーターがある場合は、実行時に問題が発生する可能性があります。プロモートされた型が引数リストを読み取るときに使用されるものと一致しない場合、可変個引数関数で同じ種類の問題が発生します。

例1:プロトタイプを使用して関数を定義し、それを使用せずに使用する場合の問題。

Definition.c

void f(char c)
{
   printf("%c", c);
}

use.c

void f();

int main()
{
   f('x');
}

intが渡され、関数がcharを予期するため、失敗する可能性があります。

例2:プロトタイプなしで関数を定義し、プロトタイプで使用する場合の問題。

Definition.c

void f(c)
   char c;
{
   printf("%c", c);
}

(これは一種の定義であり、非常に古風です)

use.c

void f(char c);

int main()
{
   f('x');
}

intが期待されているが、charが渡されるため、失敗する可能性があります。

注:標準ライブラリのすべての関数には、デフォルトのプロモーションの結果であるタイプがあることに注意してください。そのため、プロトタイプが追加されたときの移行中に問題は発生しませんでした。

于 2009-08-10T16:17:40.293 に答える
17

あなたの混乱は、用語のごくわずかな誤解から生じています-宣言と定義の両方にプロトタイプを含めることができます(または含まない):

void func(int a, char b, float c);

プロトタイプを含む関数宣言です。

void func(int a, char b, float c) { /* ... */ }

プロトタイプを含む関数定義です。

「プロトタイプ化」と「非プロトタイプ化」は単なる関数typeの属性であり、宣言と定義の両方で関数の型が導入されます。

したがって、プロトタイプなしで宣言を行うことができます。

void func();

または、プロトタイプなしで定義することもできます (K&R C スタイル):

void func(a, b, c)
    int a;
    char b;
    float c;
{ /* ... */ }
于 2009-08-11T00:30:45.560 に答える