43

ここで、C で可変引数を使用する方法の例を見つけました。

#include <stdarg.h>

double average(int count, ...)
{
    va_list ap;
    int j;
    double tot = 0;
    va_start(ap, count); //Requires the last fixed parameter (to get the address)
    for(j=0; j<count; j++)
        tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
    va_end(ap);
    return tot/count;
}

この例はある程度しか理解できません。

  1. を使用する理由は明確ではありませんva_start(ap, count);。私の知る限り、このようにしてイテレータを最初の要素に設定します。しかし、デフォルトで最初に設定されていないのはなぜですか?

  2. なぜ私たちがcount議論として与える必要があるのか​​ 、私には明らかではありません。C は引数の数を自動的に決定できませんか?

  3. を使用する理由は明確ではありませんva_end(ap)。それは何を変えますか?イテレータをリストの最後に設定しますか? しかし、ループによってリストの最後に設定されていませんか? さらに、なぜそれが必要なのですか?もう使用しapません。なぜ私たちはそれを変えたいのですか?

4

4 に答える 4

8

しかし、デフォルトで最初に設定されていないのはなぜですか?

おそらく、コンパイラが十分に賢くなかった歴史的な理由によるものでしょう。おそらく、実際には varargs を気にしない varargs 関数のプロトタイプがあり、その特定のシステムでは varargs の設定にコストがかかる可能性があるためです。おそらく、より複雑な操作が原因であるva_copyか、引数を複数回使用して作業を再開し、複数回呼び出したい場合がありますva_start

短いバージョンは次のとおりです。言語標準がそう言っているからです。

第 2 に、引数として数を指定する必要がある理由が明確ではありません。C++ は引数の数を自動的に決定できませんか?

それだけではありませんcount。これは、関数の最後の名前付き引数です。va_start可変引数がどこにあるかを把握するために必要です。これはおそらく、古いコンパイラの歴史的な理由によるものです。今日、なぜそれを別の方法で実装できなかったのかわかりません。

あなたの質問の 2 番目の部分として: いいえ、コンパイラは、関数に送信された引数の数を知りません。同じコンパイル単位または同じプログラムにさえない可能性があり、コンパイラーは関数がどのように呼び出されるかを知りません。のような可変引数関数を持つライブラリを想像してくださいprintf。libc をコンパイルするとき、コンパイラは、プログラムがいつどのように を呼び出すかを知りませんprintf。ほとんどの ABI (ABI は、関数の呼び出し方法、引数の受け渡し方法などの規則) では、関数呼び出しが取得した引数の数を調べる方法はありません。その情報を関数呼び出しに含めるのは無駄であり、ほとんど必要ありません。したがって、varargs 関数に引数の数を伝える方法が必要です。アクセスするva_arg実際に渡された引数の数を超えると、未定義の動作になります。

では、なぜ va_end(ap) を使用するのかが明確ではありません。それは何を変えますか?

ほとんどのアーキテクチャva_endでは、関連することは何もしません。ただし、複雑な引数受け渡しセマンティクスを備えたアーキテクチャがいくつかあり、va_startメモリを malloc する可能性さえある場合はva_end、そのメモリを解放する必要があります。

ここでの短いバージョンも次のとおりです。言語標準がそう言っているからです。

于 2013-04-03T13:03:12.610 に答える