C 可变参数 - va_copy 问题

6
我正在用C语言编写一个函数,该函数接受可变数量的参数。
size_t myprintf(char *fmt, ...);

到目前为止,一切都很顺利。我决定按照“正确的方式™”来做事,并制作一个接受可变参数的版本和另一个接受va_list的版本。

size_t myprintf(char *fmt, ...);
size_t myvprintf(char *fmt, va_list args);

这并不难。除了my_vprintf()需要将其args发送到两个不同的函数(首先是使用长度为0的snprintf()确定我们需要多少空间,然后在分配了那么多空间后是sprintf())。我使用va_copy来实现这一点。

size_t myvprintf(char *fmt, va_list args)
{
    va_list args2;
    va_copy(args, args2);
    // do stuff with args2
    va_end(args2);
    // do more stuff with args
}

这一切看起来都很好,但是C99的实现有些欠缺。如果可能的话,我希望我的代码也能在C89中运行,并且在尽可能多的编译器和平台上运行。我目前在#include <stddef.h>之后但在任何代码之前添加了以下内容:

#ifndef va_copy
# ifdef __va_copy
#  define va_copy(a,b) __va_copy(a,b)
# else /* !__va_copy */
#  define va_copy(a,b) ((a)=(b))
# endif /* __va_copy */
#endif /* va_copy */

我认为((a)=(b))是不可靠的,应该使用memcpy()或类似的函数,但这仅限于“如果您不支持C99,我希望它能够工作”,而不是“如果您不支持C99,无需担心”(这正是我想要的)。有没有好的方法来解决这个限制?我看到了一些解决方案- va_list函数吃掉一个参数并递归,传递两次va_list,以便生成两个单独的副本等- 但我不知道它们的效果如何(如果我只想调用vsnprintf(),递归解决方案将无法很好地解决问题,对吗?)。
因此,我向您求助,StackOverflow用户。是否有任何更好的方法提供C89兼容性,或者没有va_copy__va_copy的用户(尽管少之又少)只能接受它并承担?

现在是2009年,为什么你还需要C89兼容性呢? - stepancheg
因为VC++不支持它(而且可能永远不会支持)。但我喜欢拥有它。我认为这是一个很好的东西。 - Chris Lutz
我认为微软已经明确表示他们没有打算采用C99,除非挑选其中一些客户需要的特性。 - KTC
听起来像是微软。我会说他们应该选择适当的可变参数支持,但我并不是他们的客户之一。我想他们不支持C99也是有道理的,因为VC++是一款C++编译器而不是C编译器,但我们仍然可以梦想。 - Chris Lutz
3
不只是微软,很多编译器都不支持C99,包括gcc(虽然它几乎支持,而且已经覆盖了变参)。如果你想写标准的C代码以保证可移植性,就用C89吧。或者自己发明一种“所有我关心的编译器都支持的C99子集”。 - Steve Jessop
1个回答

3

(a)=(b) 是不可靠的。即使传递两个 va_list,公共函数也只是一个简单的包装器,因为 va_list 可能是这样的:

typedef __va_list_impl va_list[1];

如果在一个上执行va_arg,将会修改另一个(我似乎记得Solaris使用这种方式,啊,寄存器窗口...)。不幸的是,我不知道确切的方法来实现你想要的。


1
我很担心这个。哦,好吧。我想应该做的适当的事情是将我的hack-va_copy更改为#error“获取新编译器” - Chris Lutz
1
在这种情况下,va_copy宏中的赋值将失败,因为您无法将一个数组分配给另一个数组。 - caf
@ChrisLutz 你在 "Get a new compiler!" 中忘记了感叹号! :P - S.S. Anne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接