原始代码失败,因为它尝试在需要使用vprintf() 的地方使用printf()。从表面上看像logOpen 和logClose 语句这样的可疑点(鉴于符号,大概它们是打开和关闭flog 文件流的宏),代码应该是:
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_list ap2;
va_start(ap2, fmt);
vprintf(fmt, ap2);
va_end(ap2);
}
没有特别要求使用两个单独的va_list 变量;只要您在再次使用va_start() 之前使用va_end(),两次使用同一个是完全可以的。
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
当va_list 值被传递给另一个函数(此代码中的vfprintf() 和vprintf())时,您应该假设它在当前函数中不再可用。只有拨打va_end() 才安全。
此代码中不需要va_copy()。它有效,但不是必需的。在其他情况下你需要va_copy(),例如当你的函数被传递一个va_list并且你需要处理列表两次:
void logVprintf(const char *fmt, va_list args1)
{
va_list args2;
va_copy(args2, args1);
logOpen;
vfprintf(flog, fmt, args1);
logClose;
vprintf(fmt, args2);
va_end(args2);
}
请注意,在此代码中,调用代码负责在 args1 上调用 va_end()。确实,标准说:
va_start 和 va_copy 宏的每次调用
应通过在同一函数中对 va_end 宏的相应调用来匹配。
由于logVprintf() 函数没有调用va_start 或va_copy 来初始化args1,因此它不能在args1 上合法地调用va_end。另一方面,标准要求它为args2 调用va_end。
logPrintf()函数现在可以用logVprintf()来实现了:
void logPrintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
logVprintf(fmt, args);
va_end(args);
}
这种结构——一个接受va_list的操作函数和一个接受省略号(可变参数)并在转换为va_list后将它们传递给操作函数的覆盖函数——通常是一种很好的工作方式。迟早,您通常会发现需要带有 va_list 参数的版本。