7

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

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

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

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

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

4

4 回答 4

12

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

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

于 2009-11-12T11:52:42.163 回答
2

一个标准的方法是让每个可变参数函数都伴随着一个va_list-take 对应物(如在 printf 和 vprintf 中)。可变参数版本只是转换...为 a va_list(使用来自 的宏stdarg.h)并调用它的 va_list-taking 姊妹,它确实有效。

于 2009-11-12T11:54:36.777 回答
1

尝试只传递一个数组,然后使用可变参数宏可能会很有趣。根据堆栈对齐,它可能只是工作(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;
}
于 2009-11-12T11:49:59.733 回答
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]);
  /* ... */
}
于 2009-11-12T13:40:33.413 回答