28

有没有办法va_list从头开始创建?我正在尝试调用一个以 ava_list作为参数的函数:

func(void **entry, int num_args, va_list args, char *key); 

...来自一个不接受可变数量参数的函数。我能想到的唯一方法是创建一个中间函数,它接受可变参数,然后传递它的 va_list,这很愚蠢:

void stupid_func(void **entry, char *key, int num_args, ...) {
    va_list args;
    va_start(args, num_args);

    func(entry, num_args, args, key);

    va_end(args);
}

有没有更好的办法?我无法更改func的签名。

4

4 回答 4

14

这是一个坏主意,因为 va_list 抽象是为了隐藏一些关于堆栈指针的严峻的编译器/体系结构的具体细节等等。一旦初始化,它几乎就绑定到函数的范围。如果你缠绕堆栈并引用之前的帧 va_args 超出范围,事情可能会变糟。您可以传递它们,但是...

期待错误

见: http: //lists.freebsd.org/pipermail/freebsd-amd64/2004-August/001946.html

还要检查 man(3) va_copy 和朋友,以便更安全地处理 va_args 并传递它们。

恕我直言,va_args 的东西不是很整洁。在过去,我通过在堆上初始化结构/不透明指针然后使用指针算法来处理数据来处理这个问题。但这是一个黑客,取决于具体情况。

于 2009-06-12T18:42:47.540 回答
6

我理解并同意 Aiden 的警告——va_list朋友们很危险,因为他们隐藏了低级调用约定。但是......在这种情况下,我认为您别无选择。将static ...函数放入.c文件中,这样其他人就看不到它,有点像你需要调用的函数的代理,测试它,然后就完成了。只要确保您没有在调用链中暴露可变参数即可。

于 2009-06-12T19:29:39.940 回答
5

您对创建的代理函数的想法va_list是正确的方法。该代理不需要具有公共范围。但是,如果您可能发现代理已经存在。例如,在许多库实现sprintf()中只是vsprintf().

除非您愿意将代码绑定到特定的编译器和目标平台,否则没有更好的方法。中定义的名称<stdarg.h>是为了提供一个可移植且一致的接口来支持对可变参数列表的访问。实现和使用可变参数函数的唯一可移植方式是通过该接口。

也就是说,您可能会通过在数组中复制调用帧并手动构造va_list正确引用它的 a 来牺牲可移植性。结果永远不会是可移植的。

于 2009-06-12T22:17:43.577 回答
5

stupid_func是完全有效的 C 代码,否则您应该如何调用vprintf和类似的函数?

glib广泛使用这些包装器。C99 规范本身在示例中有类似的内容。节选自第7.19.6.8节:

下面显示了在一般错误报告例程中使用 vfprintf 函数。

#include <stdarg.h>
#include <stdio.h>
void error(char *function_name, char *format, ...)
{
    va_list args;
    va_start(args, format);
    // print out name of function causing error
    fprintf(stderr, "ERROR in %s: ", function_name);
    // print out remainder of message
    vfprintf(stderr, format, args);
    va_end(args)
}
于 2016-12-29T09:50:37.647 回答