假设我有一个带有可变数量参数的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);
}

其他提示

除非你想要进入顽皮和不可移植的技巧,否则无法在不知道你传递多少参数的情况下调用(例如)printf。

通常使用的解决方案是始终提供另一种形式的vararg函数,因此 printf 具有 vprintf ,它取代 va_list 代替 ... ... 版本只是 va_list 版本的包装。

Variadic Functions 可以 危险 。这是一个更安全的技巧:

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

func((type[]){val1,val2,val3,val4,0});

在华丽的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);
    }

您也可以尝试使用宏。

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

虽然您可以先将格式化程序存储在本地缓冲区中来解决格式化问题,但这需要堆栈并且有时可能会处理问题。我试过跟随它似乎工作正常。

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

希望这有帮助。

罗斯的解决方案有点清理干净了。仅当所有args都是指针时才有效。如果 __ 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);}

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

或者你呢?

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

#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()的堆栈地址读取。所以它只有在两个函数使用相同的堆栈段时才有效。

如果你给任何var作为起点,那么va_start和va_arg宏通常会起作用。因此,如果您希望您可以将指针传递给其他函数并使用它们。您可以轻松制作自己的宏。所有的宏都是类型转换内存地址。然而,使它们适用于所有编译器和调用约定是令人讨厌的。因此,使用编译器附带的内容通常更容易。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top