传递可变数量的参数

说我有一个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); } 

要传递这些省略号,你必须将它们转换为一个va_list,并在你的第二个函数中使用这个va_list。 特别;

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

变量函数可能是危险的 。 这是一个更安全的技巧:

  void func(type* values) { while(*values) { x = *values++; /* do whatever with x */ } } func((type[]){val1,val2,val3,val4,0}); 

除非你想进入淘气和不可移植的技巧,否则没有办法调用(例如)printf而不知道你传递给它多少个参数。

通常使用的解决方案是始终提供可变参数函数的替代形式,所以printfvprintf ,它取代了va_list 。 版本只是va_list版本的包装。

在宏伟的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...); } 

您可以使用内联汇编进行函数调用。 (在这个代码中我假设参数是字符)。

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

罗斯的解决方案清理了一下。 只有在所有参数都是指针的情况下才有效。 如果__VA_ARGS__为空(Visual Studio C ++和GCC),语言实现必须支持__VA_ARGS__以前的逗号。

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

你也可以试试宏。

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

假设你有一个典型的可变参数你写的。 因为在可变参数之前至少需要一个参数... ,所以在使用中必须总是写一个额外的参数。

或者你呢?

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

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

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

我不确定这是否适用于所有的编译器,但它对我来说一直工作。

 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宏通常可以工作,如果你给他们任何变种的起点。 所以,如果你想要的话,你可以传递指向其他函数的指针并使用它们。 你可以轻松地创建自己的宏。 所有的宏都是类型转换的内存地址。 但是使它们适用于所有编译器和调用约定是烦人的。 所以使用编译器附带的代码通常会更容易。