C の可変長引数の問題は、実際には言語に組み込まれておらず、後で追加されることです。主な問題は、変数パラメーターが匿名であり、ハンドルも識別子もないことです。これにより、扱いにくい VA マクロが名前のないパラメーターへの参照を生成することになります。また、可変長リストの開始位置と、パラメーターの期待される型をマクロに伝える必要も生じます。
このすべての情報は、言語自体の適切な構文でエンコードする必要があります。
たとえば、次のように、省略記号の後に仮パラメータを使用して既存の C 構文を拡張できます。
void foo ( ... int counter, float arglist );
慣例により、最初のパラメーターは引数カウント用で、2 番目のパラメーターは引数リスト用です。関数本体内では、リストを構文的に配列として扱うことができます。
このような規則では、可変引数は匿名ではなくなります。関数本体内では、カウンターは他のパラメーターと同様に参照でき、リスト要素は配列パラメーターの配列要素であるかのように参照できます。
void foo ( ... int counter, float arglist ) {
unsigned i;
for (i=0; i<counter; i++) {
printf("list[%i] = %f\n", i, arglist[i]);
}
}
このような機能が言語自体に組み込まれているため、へのすべての参照arglist[i]
は、スタック フレームのそれぞれのアドレスに変換されます。マクロを介してこれを行う必要はありません。
さらに、コンパイラによって引数カウントが自動的に挿入されるため、エラーの可能性がさらに減少します。
への呼び出し
foo(1.23, 4.56, 7.89);
書かれているかのようにコンパイルされます
foo(3, 1.23, 4.56, 7.89);
関数本体内で、実際に渡された引数の実際の数を超える要素へのアクセスは実行時にチェックされ、コンパイル時にエラーが発生する可能性があり、それによって安全性が大幅に向上しました。
最後になりましたが、すべての可変個引数は型指定されており、非可変個引数がチェックされるのと同じように、コンパイル時に型チェックできます。
コレクションにキーと値を格納する関数を作成する場合など、一部のユースケースでは、型を交互に使用することが望ましい場合があります。これは、次のように、省略記号の後に正式なパラメーターを許可するだけで対応できます。
void store ( collection dict, ... int counter, key_t key, val_t value );
この関数は、次のように呼び出すことができます。
store(dict, key1, val1, key2, val2, key3, val3);
しかし、それが書かれているかのようにコンパイルされます
store(dict, 3, key1, val1, key2, val2, key3, val3);
実際のパラメーターの型は、対応する可変個引数仮パラメーターに対してコンパイル時にチェックされます。
関数の本体内では、カウンターはその識別子によって再び参照され、キーと値は配列であるかのように参照されます。
key[i]
i 番目のキーと値のペアのキーを
value[i]
参照 i 番目の値のペアの値を参照
これらの参照は、スタック フレームのそれぞれのアドレスにコンパイルされます。
これを行うのは本当に難しいことではありません。ただし、C の設計哲学は、そのような機能を助長するものではありません。
冒険的な C コンパイラの実装者 (または C プリプロセッサの実装者) が率先してこのスキームまたは同様のスキームを実装しなければ、C でこの種のものが見られることはまずありません。
問題は、型の安全性に関心があり、独自のコンパイラを構築することに意欲的な人々は、通常、C 言語は救いようがなく、最初からより優れた設計の言語からやり直すほうがよいという結論に達することです。 .
私はそこに行ったことがありますが、最終的にはその試みを断念し、Wirth の言語の 1 つを実装し、代わりに型安全なバリアディクスを追加することにしました。それ以来、私は自分の試みが中止されたことについて私に語った他の人に出くわしました. C の適切なタイプ セーフなバリアディクスは、とらえどころのないままであると思われます。