8

これは奇妙な質問ですが、va_list別の関数に渡す前に a の内容を操作する標準的な方法はありますか? たとえば、次の 2 つの関数があるsumとしvsumます。

int vsum(int n, va_list ap) {
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total += va_arg(n, int);
    return total;
}

int sum(int n, ...) {
    va_list ap;
    va_start(ap, n);
    int total = vsum(n, ap);
    va_end(ap);
    return total;
}

sumasを呼び出すとsum(4, 1, 2, 3, 4)、結果は 10 になると予想されます。ここで、vsum直接呼び出すのではなく、次の処理をsum行う中間関数を呼び出すとします。vsum_stub

int vsum_stub(int n, va_list ap) {
    va_list temp_ap;
    va_copy(temp_ap, ap);
    for (int i = 0; i < n; ++i) {
        int *arg = &va_arg(ap, int);
        *arg += 2;
    }
    va_end(temp_ap);
    return vsum(n, ap);
}

を呼び出すと、のすべての値が 2 ずつインクリメントさsum(4, 1, 2, 3, 4)れるため、結果 20 が返されるはずです。の結果のアドレスを取得できないため、これはもちろんコンパイルされません。これを行う別の方法はありますか?私はC99で働いています。vsum_stubva_listva_arg


バックグラウンド:

データをより効率的な形式でヒープに格納できるように、ポインター変換を行うライブラリに取り組んでいます。printfプログラムは、独自のスタブ関数 (例: )などのライブラリ関数への呼び出しを変換するカスタム変換を使用してコンパイルされますhc_printf。引数を実際の関数に渡す前にhc_printf、ポインター引数 ( を対象とした文字列) を変換する必要があります。%sprintf

編集:これはコード例です。string があるとしましょうfoo。偽のポインターを返すfoo変更されたバージョンで動的に割り当てられます。mallocコンパイラは、偽のポインターを処理できるようにプログラムを変更します。したがって、これは機能します:

char *foo = fake_malloc(4);
fake_strcpy(foo, "foo");

fake_vprintfこのような関数を(疑似コードで)書きたいと思います:

int fake_vprintf(const char *format, va_list args) {
    for each pointer argument p in args
        translate p to q, a real pointer to contiguous memory
        replace p with q in args
    }
    return vprintf(format, args);
}

プログラムは、偽のポインターを使用してfake_vprintf、オリジナルと同じように呼び出します。偽のポインターを、本物が使用できる本物のポインターに変換します。vprintffake_vprintfvprintf

4

2 に答える 2

3

おそらく、プラットフォームに依存しない方法で va_list を使用することはできません。環境が stdarg.h で va_list をどのように定義しているかを確認し、それを操作するための独自のツールを作成する必要があります。

たとえば、va_list が単なる (char *) である場合、それを使用してあらゆる種類の操作を行うことができます。

// add 1000 to the integer stored on the stack and advance va_list
*(int *)va_list += 1000;
va_list += sizeof(int);

va_list を int へのポインターと見なし (int * キャストを介して)、値 (*) を取り、それに 1000 を加算 (+= 1000) するようにコンパイラーに指示しています。ここで、va_list ポインターをスタック上の次の引数に進めます。

于 2010-02-05T22:01:00.830 に答える
3

ああ、私が理解しているように、あなたの問題はva_list標準vprintf関数に渡す新しい引数を作成することです。その結果、リストの各メンバーを変更する必要があります。ただし、そのようなリストには要素ごとのフェッチ/編集/挿入操作がないため、スタックします。

これを行う方法が本当にわかりません。もちろん、一度に 1 つの引数で、その場vprintfでの変換の適用を作成できます。私の提案は次のとおりです。そのような標準ライブラリ関数をすべて再実装します-とにかく、ラッパーを作成しています。これにはいくつかの作業が伴いますが、すでに etc でその一部を行っているので、最後まで行ってみませんか (関数呼び出しで何が節約できるかを推測してください!)。hc_printf

于 2010-02-05T21:15:55.183 に答える