13

ジェネリック型で C stdarg.h lib を使用しようとしています。型 int は、私の一般的な型です > それを理解するには、読んでお待ちください。だから、私の問題は次のとおりです。

可変数の引数を受け入れる関数があります。お気に入り

void function (int paramN, ...);

私のプログラムでは、可変引数のタイプを知る方法はありません。char、配列、int、short、関数ポイントなどの可能性があります...のように

function (paramN, "Hey, I'm a string", 1, function_pint, array, -1); // -1 is a sentinel.

したがって、intは32ビットで、x86(32ビット)システムでは、これがメモリのすべてのアドレスを保持すると思います。ですから、すべての引数を int で取得する場合は問題ありません。キャスト。

私は正しい?
私はそれを行うことができますか?
注: 自分の関数を printf のようにしたくありません (この解決策は、この場合には当てはまりませんか?)

すべての答えをありがとう。
そして私の悪い英語でごめんなさい。

4

6 に答える 6

14

各パラメーターに void * (または型付き構造) を使用し、「型」引数 (整数) を持つ構造を使用します。実際の値を含むポインター/共用体。

つまり、各パラメーターは、型付き構造体へのポインターと共に渡されます。この型付き構造体の各インスタンスには値が含まれます。この「値」の型は、この型付き構造に含まれています。

より良い例:

typedef struct  {
  int type;
  union {
    int int_value;
    double double_value;
    ...
  };
} Param;

void function(Param *p1, Param *p2, ...)

私が遭遇したそのようなトリックの最新の例は DBus でした。

于 2009-11-06T17:07:56.817 に答える
12

あなたが説明した方法でそれを行うことはできません。

C 呼び出し規則は、呼び出し元がスタックに引数を配置するためのものですが、型に関する情報は何も配置しないため、呼び出し先はそれを見つける方法 (少なくとも変数のサイズ) を持っている必要があります。

  • すべてのタイプが既知のプロトタイプを持つ関数については問題ありません。

  • 変数番号またはパラメーター (変数) を持つ関数では、よりトリッキーです。各変数を読み取るには、引数ごとに va_arg を呼び出す必要があり、va_arg の型を指定する必要があります。実際の型ではない型を指定した場合、コンパイラは文句を言いませんが (ランタイム情報ではありません)、何かが起こる可能性があります (通常は何か悪いことです)。

したがって、型渡す必要があります。

型を予測できる場合もありますが (例: カウンタの後にいくつかの int が続くなど)、通常はパラメータでエンコードして渡します。printf のようにフォーマット文字列でエンコードして渡すことができます。jldupont で説明されているユニオン トリックも十分に一般的です。

しかし、とにかく合格しなければなりません。

データサイズでさえ、基礎となるデータのバイナリ表現に頼ることはできません。プログラムを書いたときに動作しているように見えても、互換性がなく、システムやコンパイラを変更したり、コンパイル オプションを変更したりすると壊れる可能性があります。

型の後に引数を渡す例を見てみましょう (したがって、ユニオン トリックも printf のような書式文字列もありません)。それが行うことは、渡されたすべての値を double に変換して追加することですが、実際には役に立ちません。

#include <stdio.h>
#include <stdarg.h>

enum mytypes {LONG, INT, FLOAT, DOUBLE };

double myfunc(int count, ...){
    long tmp_l;
    int tmp_i;
    double tmp_d;
    double res = 0;
    int i;

    va_list ap;
    va_start(ap, count);
    for(i=0 ; i < count; i++){
        int type = va_arg(ap, enum mytypes);
        switch (type){
            case LONG:
            tmp_l = va_arg(ap, long);
            res += tmp_l;
            break;
            case INT:
            tmp_i = va_arg(ap, int);
            res += tmp_i;
            break;
            case FLOAT:
            /* float is automatically promoted to double when passed to va_arg */
            case DOUBLE:
            tmp_d = va_arg(ap, double);
            res += tmp_d;
            break;
            default: /* unknown type */
            break;
        }
    }
    va_end(ap);
    return res;
}

int main(){
    double res;
    res = myfunc(5,
        LONG, (long)1,
        INT, (int)10,
        DOUBLE, (double)2.5,
        DOUBLE, (double)0.1,
        FLOAT, (float)0.3);
    printf("res = %f\n", res);
}

この例では、C99 で定義された新しいstdarg 可変長ヘッダーを使用しています。これを使用するには、関数に少なくとも 1 つの固定パラメーターが必要です (この例ではcount)。したがって、関数にいくつかの可変長リストを含めることができれば良いことです(つまり、 のようなものmyfunc(int count1, ..., int count2, ...))。悪い点は、古い形式のように、純粋に可変長関数 (つまり、myfunc(...) のようなもの) を持つことができないことです。varargs互換ヘッダーを使用して、古い形式を引き続き使用できます。しかし、それはより複雑であり、ほとんど必要ありません。 、タイプが必要なだけでなく、リストが終了したことを知る方法やカウントのようなものが便利だからです(ただし、それを行う唯一の方法ではありません。たとえば、「ターミネーター」を使用できます)。

于 2009-11-06T17:51:28.403 に答える
3

明示的に行わない限り、渡す引数の型に関する情報はコンパイル後に保持されないため、記述している方法で可変引数を使用してこれを行うことはできません。引数変数のアドレスを渡しても、これはわかりません。文字列を表すメモリ内のビットは、数値またはその他のものを表す可能性があるためです。

varargs を変数型で機能させるには、型情報を引数自体に格納するか (たとえば、jldupont の回答で説明されているように)、情報を非変数引数 (たとえば、のような書式文字列) に格納できますprintf。 )。

于 2009-11-06T17:19:30.263 に答える
1

http://en.wikipedia.org/wiki/Stdarg.hからの対処:

関数に渡される名前のない引数の [...] 型を決定するメカニズムは定義されていません。関数は、これを何らかの方法で知るか決定するために必要なだけであり、その手段はさまざまです。

つまり、関数は引数だけではどの引数が文字列であるかを知ることができません。どういうわけか、関数に何を期待するかを伝える必要があります。そのため、printfコンベンションは非常に一般的です。

于 2009-11-06T17:23:18.367 に答える
0

stdint.h で定義されている u?intptr_t を試してください。これは、ポインターを保持するのに十分な大きさであることが保証されている整数です。

浮動小数点数を渡すとどうなるかについても考えてみてください。double に変換されて渡されます。プログラムが int を予期している場合、スタックのビューが破棄されます。ああ。そして、コンパイラはこれをキャッチできません。

そして、定義されているように、関数には(paramNのビットエンコーディングを禁止します。この場合、列挙型またはビットフィールドであるか、少なくとも符号なしである必要があります)、受け取るパラメーターのタイプが何であるかを知る方法はありません。そのようなものは、それらに対して何か有用なことを行うことができる可能性は低いです. C には、そのオブジェクトに関する実行時の型情報がありません。

正確には、何をしようとしているのですか?

于 2009-11-06T17:29:41.147 に答える