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

问题描述:

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

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

,我接受了一堆数值从用户,我想这些值传递到函数的一些方法:

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

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

+1

如果所有值均为同一类型(如你的问题似乎暗示 - 纠正我,如果我错了)我建议不要使用可变参数函数,而是传递一个数组。看到这里的一些宏魔法漂亮起来,当你想传递固定数量的参数:http://*.com/questions/1375474/variable-arity-in-c/1375636#1375636 – Christoph 2009-11-12 11:59:38

+0

不,他们是不同的类型。 – tommobh 2009-11-12 12:01:32

+0

@tommobh:如果你将值包装在union中,或者将'void *'数组传递给值而不是值本身,但数组方法仍然可以工作 – Christoph 2009-11-12 12:04:10

尝试传递一个数组可能会很有趣,然后使用可变参数宏。根据堆栈对齐情况,它可能正常工作(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; 
} 
+0

它不会工作,因为数组没有通过值传递,但作为指针 – Christoph 2009-11-12 11:55:45

+0

正确 - 当我想出来的时候,我想不知何故推动堆栈上的数组。但是如果你只是将数组传递给一个函数,C就不会这么做 - 你必须自己去做。 – gnud 2009-11-12 11:58:06

+0

当使用一个结构来包装数组时,它有可能工作,至少:)参见示例 – gnud 2009-11-12 13:16:40

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

+0

@Anacrolix你是什么意思“并适当调用可变参数调用”?该函数处理可变参数。 – tommobh 2009-11-12 12:28:00

+0

@tommobh:我编辑解释 – 2009-11-12 12:33:01

+0

@Anacrolix非常感谢,非常感谢。 – tommobh 2009-11-12 12:55:15

部分

一个标准的方法是让每个可变参数函数伴随着一个va_list评论对象(如在printf和vprintf中)。该可变参数版本只是将...转换为va_list(使用来自stdarg.h的宏),并调用其实际工作的va_list-sister。

+0

谢谢,我会研究这种方法。 – tommobh 2009-11-12 12:06:26

+0

@atzz虽然同样的问题。我仍然需要动态地将参数添加到该函数,无论它是否调用其va_list-姐妹。你的意思是我将这些值转换为va_list,然后使用va_list-taking姐妹函数来代替? – tommobh 2009-11-12 12:54:45

+0

@tommobh - 我想我错误地解释了你的问题。通过阅读评论,我认为你有一种获得不同类型值的方法(例如,来自gui),并且需要将它们传递给一个函数;这是对的吗?在这种情况下,va_list不会有太大的帮助。如果你不想依赖黑客,你将不得不改变功能... – atzz 2009-11-12 16:10:37

取决于你传递的是什么,它可能是你在此之后的一个有区别的联盟(正如评论中暗示的那样)。这将避免需要可变参数函数或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]); 
    /* ... */ 
} 
+0

@Ned:我正在使用的可变参数函数是在一个库中,我最好不喜欢改变(即我不想要改变'some_function()')。用户可以输入他们喜欢的任何类型的数量(在合理范围内),因此定义严格的联合似乎不会起作用。一系列void *或Anacrolix的建议似乎是我最好的选择。无论是我还是我写一些内联汇编:S。 – tommobh 2009-11-12 13:52:15

+0

但是,如果你不能改变some_function,你怎么能通过它一个void *数组?用户在这里,我们是在讨论一个使用你的代码的开发者,或者是该程序的最终用户? – Ned 2009-11-12 13:56:34

+0

@Ned:User =最终用户。是的,我明白你的意思了。我不想改变函数,因为函数本身调用另一个可变参数函数,我也必须改变它。它只会雪球。我感觉我回到了我开始的地方。 – tommobh 2009-11-12 14:14:16