重复使用可变参数函数参数不起作用
我有一个函数,试图将东西记录到控制台和日志文件,但它不起作用。 第二次使用可变长度参数会将垃圾写入控制台。 有任何想法吗?
void logPrintf(const char *fmt, ...) { va_list ap; // log to logfile va_start(ap, fmt); logOpen; vfprintf(flog, fmt, ap); logClose; va_end(ap); va_list ap2; // log to console va_start(ap2, fmt); printf(fmt, ap2); va_end(ap2); }
原始代码失败,因为它尝试使用printf()
,它需要使用vprintf()
。 将logOpen
和logClose
语句等可疑点logOpen
面值(给定符号,可能是它们是打开和关闭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()
函数现在可以用logPrintf()
实现:
void logPrintf(const char *fmt, ...) { va_list args; va_start(args, fmt); logVprintf(fmt, args); va_end(args); }
这个结构 – 一个操作函数,它接受一个带有省略号(变量参数)的va_list
和一个封面函数,并在转换为va_list
后将它们传递给操作函数 – 通常是一种很好的工作方式。 迟早,您通常会发现需要具有va_list
参数的版本。
升级你的编译器,更像是C ++:
template void logPrintf(const char *fmt, Args&&... args) { logOpen; fprintf(flog, fmt, args...); logClose; printf(fmt, args...); }
当然,提供printf
和fprintf
类型安全版本当然是个好品味。
我认为这种方式更有意义:
void logPrintf(const char *fmt, ...) { va_list ap; // log to logfile va_start(ap, fmt); logOpen; vfprintf(flog, fmt, ap); //logfile printf(fmt, ap); //console logClose; va_end(ap); }