6

C では、次のような可変長の引数リストを持つ関数を許可しないのはなぜですか。

void f(...)
{
    // do something...
}
4

6 に答える 6

10

varargs 関数に名前付きパラメーターが必要であるという要件の動機は、の均一性のためだと思いますva_start。実装を容易にするためva_startに、最後に名前が付けられたパラメーターの名前を取ります。典型的な varargs 呼び出し規則では、引数が格納されている方向に応じて、 addressまたはでva_arg最初の vararg が検索され、アラインメントを確保するためにプラスまたはマイナスのパディングが行われます。(&parameter_name) + 1(first_vararg_type*)(&parameter_name) - 1

言語が名前付きパラメーターのない varargs 関数をサポートできなかった特別な理由はないと思います。そのような関数で使用するための代替形式が必要va_startであり、スタックポインターから直接最初の vararg を取得する必要があります (または、スタックポインターが関数に持っていた値であるフレームポインターをペダンティックにする必要があります)。関数内のコードは、関数のエントリ以降に sp を移動した可能性があるためです)。これは原則として可能です。どの実装でも、あるレベルで何らかの方法でスタック [*] にアクセスできる必要がありますが、一部の実装者にとっては煩わしい場合があります。varargs の呼び出し規則がわかれば、一般にva_、他の実装固有の知識がなくてもマクロを実装できます。また、呼び出し引数を直接取得する方法も知っています。以前、エミュレーション レイヤーでこれらの varargs マクロを実装したことがあります。

また、名前付きパラメーターのない varargs 関数の実用的な用途はあまりありません。変数引数の型と数を決定する varargs 関数の言語機能はないため、呼び出し先は最初の vararg の型を読み取って読み取る必要があります。したがって、タイプを持つ名前付きパラメーターにすることもできます。printfand friends では、最初のパラメーターの値は、可変引数の型とその数を関数に伝えます

理論的には、呼び出し先は最初の引数を読み取る方法 (およびその引数があるかどうか) を理解するためにグローバルを調べることができると思いますが、それはかなり厄介です。私は確かにそれをサポートするためにわざわざ行くつもりはありませんし、追加のva_start実装負担を伴う の新しいバージョンを追加することは私の方法ではありません.

[*] または、実装がスタックを使用しない場合は、関数の引数を渡すために代わりに使用するものに。

于 2011-07-04T11:07:06.363 に答える
9

可変長の引数リストでは、最初の引数の型を宣言する必要があります。これが言語の構文です。

void f(int k, ...)
{
    /* do something */
}

うまくいきます。次に、関数内va_listva_startva_end、 などを使用して、個々の引数にアクセスする必要があります。

于 2011-07-04T10:52:12.700 に答える
1

それをいじってみると、一部の人々が検討したいと思うかもしれないこの素晴らしい実装ができました。

template<typename T>
void print(T first, ...)
{
    va_list vl;
    va_start(vl, first);
    T temp = first;
    do
    {
        cout << temp << endl;
    }
    while (temp = va_arg(vl, T));
    va_end(vl);
}

これにより、変数の最小値が 1 つ確保されますが、それらすべてをきれいな方法でループに入れることができます。

于 2014-10-02T03:30:43.000 に答える
1

C では可変長の引数を使用できますが、va_list、va_start、va_end などを使用する必要があります。printfとその仲間はどのように実装されていると思いますか? そうは言っても、私はそれをお勧めしません。通常、パラメーターに配列または構造体を使用すると、同様のことをよりきれいに行うことができます。

于 2011-07-04T10:51:45.967 に答える
0

C が void f(...) を受け入れられない本質的な理由はありません。可能でしたが、この C 機能の「設計者」はそうしないことにしました。

彼らの動機についての私の推測では、void f(...) を許可すると、許可しない場合よりも多くの「隠し」コード (ランタイムとして説明できる) が必要になるということです。 ) (およびその他)、C は与えられた引数の数をカウントする方法を提供する必要があり、これにはさらに生成されたコードが必要です (おそらく新しいキーワードまたはカウントを取得するための「nargs」などの特別な変数)、および C は通常、できるだけミニマリストになろうとします。

于 2011-07-04T11:27:50.523 に答える
-2

...引数を許可しません。つまりint printf(const char *format, ...);、ステートメントの場合

printf("foobar\n");

有効です。

少なくとも 1 つのパラメーター (より多くのパラメーターをチェックするために使用する必要があります) を強制しない場合、関数がどのように呼び出されたかを "知る" 方法はありません。

これらのステートメントはすべて有効です

f();
f(1, 2, 3, 4, 5);
f("foobar\n");
f(qsort);
于 2011-07-04T11:03:24.947 に答える