我要在旧的嵌入式平台上引入第3方协议栈,在该平台上,除了va_copy之外,所有va_ *东西都已实现。我面临的问题是,在第三方堆栈中,使用了vsnprintf():
int fun(char **buf, size_t buf_size, const char *fmt, va_list ap) {
va_list ap_copy;
int len;
/* first call*/
va_copy(ap_copy, ap);
len = vsnprintf(*buf, buf_size, fmt, ap_copy);
va_end(ap_copy);
if(len >= buf_size)
{
/* 2nd call*/
va_copy(ap_copy, ap);
len = vsnprintf(*buf, len + 1, fmt, ap_copy);
va_end(ap_copy);
}
}
幸运的是,第三方堆栈证明了自己的vsnprintf函数(称为new_vsnprintf),但是如果没有va_copy,则只有第一个调用起作用,即,当len小于buf_size时。下面是我的称呼方式:
#define vsnprintf new_vsnprintf
int fun(char **buf, size_t buf_size, const char *fmt, va_list ap) {
//va_list ap_copy;
int len;
/* first call*/
va_start(ap, fmt);
len = vsnprintf(*buf, buf_size, fmt, ap);
va_end(ap);
if(len >= buf_size)
{
/* 2nd call*/
va_start(ap, fmt);
len = vsnprintf(*buf, len + 1, fmt, ap); //new_vsnprintf()
va_end(ap);
}
}
尝试通过va_arg()
获取占位符的实际值时,在new_vsnprintf()的第二次调用中出现问题。我假设(va_list)ap的内部指针指向错误的内存地址。
那么如何纠正呢?
答案 0 :(得分:1)
如果您的平台像大多数平台一样具有简单的varargs处理,则此答案可能有用。如果您的平台不允许简单复制va_list对象,则此操作将无效。在大多数平台上,va_copy是一个简单的宏,其功能类似于:
#define va_copy(dest, src) dest = src
... x86,ppc和许多其他目标就是这种情况。无论如何,这里是代码,我希望它能工作,在不知道您使用它的平台的情况下,我无法测试或确认它能工作!
int fun(char **buf, size_t buf_size, const char *fmt, va_list ap) {
va_list ap_copy = ap;
int len;
/* first call*/
va_start(ap_copy, fmt);
len = vsnprintf(*buf, buf_size, fmt, ap_copy);
va_end(ap_copy);
if(len >= buf_size)
{
/* 2nd call*/
va_start(ap, fmt);
len = vsnprintf(*buf, len + 1, fmt, ap); //new_vsnprintf()
va_end(ap);
}
}
答案 1 :(得分:1)
您不应在函数va_start
中调用va_end
或fun
。仅应在参数列表中具有...
的函数中调用它们,以指示参数数量可变。如果我尝试编译当前存在的代码,我的gcc版本(4.8.4)甚至会引发错误。
test.c: In function ‘fun’:
test.c:14:3: error: ‘va_start’ used in function with fixed args
va_start(ap, fmt);
在带有固定args的函数中调用它们似乎导致未定义的行为,很幸运的是,对vsnprintf
的第一次调用确实有效。
由于您没有在va_arg
内部调用fun
,因此ap
的值不应更改,并且您应该可以多次调用vsnprintf
要求。