代码之家  ›  专栏  ›  技术社区  ›  Vicent Marti

传递可变数量的参数

  •  316
  • Vicent Marti  · 技术社区  · 17 年前

    假设我有一个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);
     }
    
    10 回复  |  直到 7 年前
        1
  •  198
  •   Servy    12 年前

    要传递椭圆,必须将它们转换为va_列表,并在第二个函数中使用该va_列表。明确地;

    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);
    }
    
        2
  •  54
  •   Mike F    17 年前

    如果不知道要传递多少参数,就无法调用printf,除非你想使用淘气的、不可移植的技巧。

    通常使用的解决方案是始终提供vararg函数的另一种形式,因此 printf vprintf 这需要一个 va_list 代替 ... . 这个 版本只是围绕 VAI表 版本。

        3
  •  51
  •   Community Mohan Dere    9 年前

    Variadic Functions 可以是 dangerous . 下面是一个更安全的技巧:

       void func(type* values) {
            while(*values) {
                x = *values++;
                /* do whatever with x */
            }
        }
    
    func((type[]){val1,val2,val3,val4,0});
    
        4
  •  27
  •   user2023370    15 年前

    在华丽的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...);
    }
    
        5
  •  7
  •   Yoda    14 年前

    可以对函数调用使用内联程序集。(在这段代码中,我假设参数是字符)。

    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);
        }
    
        6
  •  4
  •   VarunG    7 年前

    虽然您可以通过首先将格式化程序存储在本地缓冲区来解决传递格式化程序的问题,但这需要堆栈,有时可能是需要处理的问题。我试着跟在后面,好像效果不错。

    #include <stdarg.h>
    #include <stdio.h>
    
    void print(char const* fmt, ...)
    {
        va_list arg;
        va_start(arg, fmt);
        vprintf(fmt, arg);
        va_end(arg);
    }
    
    void printFormatted(char const* fmt, va_list arg)
    {
        vprintf(fmt, arg);
    }
    
    void showLog(int mdl, char const* type, ...)
    {
        print("\nMDL: %d, TYPE: %s", mdl, type);
    
        va_list arg;
        va_start(arg, type);
        char const* fmt = va_arg(arg, char const*);
        printFormatted(fmt, arg);
        va_end(arg);
    }
    
    int main() 
    {
        int x = 3, y = 6;
        showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
        showLog(1, "ERR");
    }
    

    希望这有帮助。

        7
  •  3
  •   Jagdish    11 年前

    您也可以尝试宏。

    #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;
    }
    
        8
  •  1
  •   rubik    11 年前

    罗斯的溶液洗了一点。只有当所有参数都是指针时才有效。另外,语言实现必须支持省略前面的逗号,如果 __VA_ARGS__ 是空的(VisualStudioC++和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);}
    
        9
  •  -1
  •   Engineer    10 年前

    假设你有一个典型的变量函数。因为变量之前至少需要一个参数 ... ,在使用中必须始终编写一个额外的参数。

    或者你呢?

    如果将变量函数包装在宏中,则不需要前面的参数。考虑这个例子:

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

    这显然要方便得多,因为您不必每次都指定初始参数。

        10
  •  -5
  •   Jim    13 年前

    我不确定这是否适用于所有的编译器,但迄今为止它对我有效。

    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);
    }
    

    您可以添加…如果你想的话,可以放在里面,但你不需要它。这是因为va_start使用给定变量的地址作为起点。在本例中,我们给它一个对func()中变量的引用。所以它使用这个地址并在堆栈上读取该地址之后的变量。内部函数func()从func()的堆栈地址读取。因此,只有当两个函数使用相同的堆栈段时,它才起作用。

    如果将任何var作为起点,va_start和va_arg宏通常都可以工作。因此,如果需要,您可以将指针传递给其他函数并使用这些函数。您可以很容易地生成自己的宏。所有的宏都是类型化内存地址。然而,让它们适用于所有编译器和调用约定是令人讨厌的。因此,使用编译器附带的那些通常比较容易。