将参数动态传递给可变参数函数

时间:2009-11-12 11:45:53

标签: c dynamic argument-passing variadic

我想知道是否有任何方法可以将参数动态传递给可变参数函数。即如果我有一个功能

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

我正在接受用户的一堆值,我想要一些方法将这些值传递给函数:

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

我不想写所有这些功能的不同版本,但我怀疑没有其他选择?

4 个答案:

答案 0 :(得分:11)

Variadic函数使用一个调用约定,其中调用者负责从堆栈中弹出函数参数,所以是的,可以动态地执行此操作。它在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);

您可能还会发现this link讨论在C中围绕可变函数生成包装器,以证明为什么它不是标准C的一部分。

答案 1 :(得分:2)

标准方法是让每个可变参数函数伴随va_list - 采用对应物(如printf和vprintf)。可变版本只是将...转换为va_list(使用来自stdarg.h的宏)并调用其va_list-taking姐妹,它可以完成实际工作。

答案 2 :(得分:1)

尝试传递数组可能会很有趣,然后再使用vararg宏。根据堆栈对齐情况,它可能只是工作(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;
}

答案 3 :(得分:0)

根据你传递的内容,它可能是你在此之后的一个有区别的联盟(正如评论中所暗示的那样)。这样可以避免使用void*的可变函数或数组,并回答问题“ some_function如何知道你实际传递了什么”。你可能有这样的代码:

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执行有用功能的函数的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]);
  /* ... */
}