7

パラメータを可変引数関数に動的に渡す方法があるかどうか疑問に思っていました。つまり、関数がある場合

int some_function (int a, int b, ...){/*blah*/}

そして、ユーザーから一連の値を受け入れています。これらの値を関数に渡す方法が必要です。

some_function (a,b, val1,val2,...,valn)

これらすべての関数の異なるバージョンを書きたくありませんが、他に選択肢はないと思いますか?

4

4 に答える 4

12

可変個引数関数は、呼び出し元がスタックから関数パラメーターをポップする責任がある呼び出し規約を使用するため、そうです、これを動的に行うことができます。これはCで標準化されておらず、通常、必要なパラメーターを手動でプッシュし、可変個引数関数を正しく呼び出すために、いくつかのアセンブリが必要になります。

呼び出し規約ではcdecl、引数が正しい順序でプッシュされ、呼び出し後、呼び出しがポップされる前にバイトが引数としてプッシュされる必要があります。このように、呼び出し元はスタックポインターを呼び出し前の状態に戻す処理を行うため、呼び出された関数は任意の数のパラメーターを受け取ることができます。の前の引数が占めるスペース...は、プッシュされるバイト数の安全な下限です。追加の可変個引数は実行時に解釈されます。

FFCALLは、パラメータを可変個引数関数に動的に渡すためのラッパーを提供するライブラリです。興味のある関数のグループはavcallです。上記で指定した関数を呼び出す例を次に示します。

#include <avcall.h>

av_alist argList;
int retVal;
av_start_int(argList, some_function, retval);
av_int(argList, a);
av_int(argList, b);
av_type(argList, val1);
...
av_type(argList, valn);
av_call(argList);

また、Cで可変個引数関数のラッパーを生成する方法について説明しているこのリンクは、これが標準Cの一部ではない理由を正当化するのに役立ちます。

于 2009-11-12T11:52:42.163 に答える
2

標準的なアプローチは、va_list(printfやvprintfのように)各可変個引数関数に対応するものを付けることです。可変個引数バージョンは(からのマクロを使用して)に変換...し、実際の作業を行うva_list-takeing姉妹を呼び出します。va_liststdarg.h

于 2009-11-12T11:54:36.777 に答える
1

配列を渡すだけで、とにかくvarargマクロを使用するのは興味深いかもしれません。スタックの配置によっては、Just Work(tm)の場合があります。

これはおそらく最適な解決策ではありません。私はそのアイデアが面白いと思ったので、主に投稿しました。試してみたところ、このアプローチは私のLinux x86では機能しましたが、x86-64では機能しませんでした。おそらく改善できるでしょう。この方法は、スタックアライメント、構造アライメントなどに依存します。

void varprint(int count, ...)
{
    va_list ap;
    int32_t i;

    va_start(ap, count);
    while(count-- ) {
        i = va_arg(ap, int32_t);
        printf("Argument: %d\n", i);
    }
    va_end(ap); 
}

struct intstack
{
    int32_t pos[99];
};

int main(int argc, char** argv)
{
    struct intstack *args = malloc(sizeof(struct intstack));
    args->pos[0] = 1;
    args->pos[1] = 2;
    args->pos[2] = 3;
    args->pos[3] = 4;
    args->pos[4] = 5;

    varprint(5, *args);
    return 0;
}
于 2009-11-12T11:49:59.733 に答える
0

あなたが回っているものに応じて、それはあなたがここで求めている差別された組合である可能性があります(コメントで示唆されているように). これにより、可変長関数または の配列が不要になり、「 some_function は実際に渡したものをどのように知るvoid*のか」という質問に答えます。次のようなコードがあるかもしれません:

enum thing_code { INTEGER, DOUBLE, LONG };

struct thing
{
 enum thing_code code;
 union
 {
    int a;
    double b;
    long c;
 };
};

void some_function(size_t n_things, struct thing *things)
{
    /* ... for each thing ... */
    switch(things[i].code)
    {
      case INTEGER:
      /* ... */
    }
}

これをさらに一歩進めて、 をcode、それぞれで何か役立つことを行う関数への 1 つ以上のポインターに置き換えることで、切り替えを回避できますthing。たとえば、単純にそれぞれを印刷したい場合は、次のようにすることができます。

struct thing
{
 void (*print)(struct thing*);
 union
 {
   ...
 };
}

void some_function(size_t n_things, struct thing *things)
{
  /* .. for each thing .. */
  things[i]->print(things[i]);
  /* ... */
}
于 2009-11-12T13:40:33.413 に答える