347

可変数の引数をとる C 関数があるとします: 内部から可変数の引数を期待する別の関数を呼び出して、最初の関数に渡されたすべての引数を渡すにはどうすればよいでしょうか?

例:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }
4

11 に答える 11

222

va_list省略記号を渡すには、通常どおりa を初期化し、それを 2 番目の関数に渡すだけです。使用しませんva_arg()。具体的には;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}
于 2008-10-15T17:11:06.840 に答える
62

いたずらで移植性のないトリックを使いたくない場合を除き、渡す引数の数を知らずに (たとえば) printf を呼び出す方法はありません。

printf一般的に使用される解決策は、常に vararg 関数の代替形式を提供するvprintfことva_listです...。バージョンは、バージョン...の単なるラッパーva_listです。

于 2008-10-15T17:11:50.473 に答える
53

可変長関数危険な場合があります。より安全なトリックは次のとおりです。

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});
于 2011-11-27T04:51:44.250 に答える
29

壮大な C++0x では、可変個引数テンプレートを使用できます。

template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
  format_string(fmt, ts...);
}
于 2011-03-24T22:14:54.213 に答える
8

関数呼び出しにインライン アセンブリを使用できます。(このコードでは、引数は文字であると想定しています)。

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }
于 2011-07-26T19:40:49.893 に答える
7

マクロも試すことができます。

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}
于 2015-04-19T14:23:23.770 に答える
2

ロスのソリューションは少しきれいになりました。すべての引数がポインターである場合にのみ機能します。また、言語の実装では、 が空の場合、前のコンマの省略をサポートする必要__VA_ARGS__があります (Visual Studio C++ と GCC の両方でサポートされています)。

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
于 2012-10-05T23:49:35.663 に答える
0

あなたが書いた典型的な可変関数があるとしましょう。可変引数の前に少なくとも 1 つの引数が必要なため...、使用法では常に追加の引数を記述する必要があります。

それともあなたですか?

可変個引数関数をマクロでラップする場合、先行する引数は必要ありません。次の例を検討してください。

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

毎回初期引数を指定する必要がないため、これは明らかにはるかに便利です。

于 2015-07-28T11:42:46.043 に答える
-5

これがすべてのコンパイラで機能するかどうかはわかりませんが、これまでのところ機能しています。

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

必要に応じて ... を inner_func() に追加できますが、必要ありません。va_start は指定された変数のアドレスを開始点として使用するため、機能します。この場合、func() で変数への参照を与えています。したがって、そのアドレスを使用して、その後スタック上の変数を読み取ります。inner_func() 関数は、func() のスタック アドレスから読み取ります。したがって、両方の関数が同じスタック セグメントを使用する場合にのみ機能します。

va_start および va_arg マクロは、開始点として var を指定すると、通常は機能します。したがって、必要に応じて、他の関数にポインターを渡し、それらも使用できます。独自のマクロを簡単に作成できます。マクロが行うのは、メモリ アドレスの型キャストだけです。ただし、それらをすべてのコンパイラと呼び出し規約で機能させるのは面倒です。そのため、コンパイラに付属しているものを使用する方が一般的に簡単です。

于 2013-05-08T21:30:33.700 に答える