重复使用可变参数函数参数不起作用

我有一个函数,试图将东西记录到控制台和日志文件,但它不起作用。 第二次使用可变长度参数会将垃圾写入控制台。 有任何想法吗?

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() 。 将logOpenlogClose语句等可疑点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_startva_copy宏的每次调用都应与同一函数中va_end宏的相应调用相匹配。

由于logVprintf()函数不调用va_startva_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...); } 

当然,提供printffprintf类型安全版本当然是个好品味。

我认为这种方式更有意义:

 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); }