有没有办法从头开始创建va_list
?我正在尝试调用一个以va_list
作为参数的函数:
func(void **entry, int num_args, va_list args, char *key);
...来自不采用可变数量参数的函数。我能想到的唯一方法是创建一个中间函数,它接受varargs然后传递它的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
的签名。
答案 0 :(得分:14)
这是一个坏主意,因为va_list抽象是为了隐藏一些关于堆栈指针的 grim 编译器/体系结构特定细节,而不是。一旦初始化,它几乎与函数的范围有关。如果你缠绕堆栈并引用前一帧va_args超出范围,事情可能会变坏。你可以传递它们但是......
期待错误
请参阅:http://lists.freebsd.org/pipermail/freebsd-amd64/2004-August/001946.html
还要检查man(3)va_copy和朋友,以便更安全地处理va_args并传递它们。
恕我直言va_args的东西不是很整洁。在过去,我通过初始化堆上的结构/不透明指针然后使用指针算法来处理数据来解决这个问题。但这是一个黑客,取决于具体情况。
答案 1 :(得分:6)
我理解并同意艾登的警告 - va_list
并且朋友很危险,因为他们隐藏了低级别的召唤惯例。但是......在这种情况下,我认为你别无选择。将static ...
函数放入.c
文件中,以便其他任何人都无法看到它,对您需要调用的函数进行代理,测试它的完整性并完成。只要确保你没有在调用链中公开可变参数。
答案 2 :(得分:5)
您创建va_list
的代理函数的想法是正确的方法。该代理不需要具有公共范围。但是,如果您可能发现代理已存在。例如,在许多库实现中,sprintf()
只是vsprintf()
的代理。
除非您愿意将代码绑定到特定的编译器和目标平台,否则没有更好的方法。 <stdarg.h>
中定义的名称用于提供可移植且一致的接口,以支持对可变参数列表的访问。实现和使用可变参数函数的唯一可移植方法是通过该接口。
也就是说,您可能会通过复制数组中的调用帧来牺牲可移植性,并手工构造一个正确引用它的va_list
。结果将永远不可移植。
答案 3 :(得分:4)
在cocoawithlove上有一篇有用的博客文章提示了伪造va_list的方法 - 它确实将你锁定在编译器和平台特定的行为上,正如其他好的答案所指出的那样。
答案 4 :(得分:3)
您的stupid_func
是完全有效的C代码,否则您应该如何调用vprintf
和类似函数?
glib
library广泛使用这些包装。 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) }