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

时间:2012-02-16 10:07:34

标签: c++ c variadic variadic-functions

我有一个函数尝试将内容记录到控制台以及日志文件中,但它不起作用。第二次使用变长参数会将垃圾写入控制台。有任何想法吗?

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

3 个答案:

答案 0 :(得分:11)

原始代码失败,因为它尝试在需要printf()的地方使用vprintf()。将logOpenlogClose语句等可疑点视为面值(给定符号,可能是它们是打开和关闭flog文件流的宏),代码应为:< / p>

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_end()之前使用va_start(),就可以使用相同的两次。

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

请注意,在此代码中,调用代码有责任在va_end()上调用args1。的确,标准说:

  

每次调用va_startva_copy宏   应与相同函数中va_end宏的相应调用相匹配。

由于logVprintf()功能无法调用va_startva_copy来初始化args1,因此无法在va_end上合法调用args1 。另一方面,该标准要求它为va_end调用args2

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参数的版本。

答案 1 :(得分:2)

升级您的编译器,更像是C ++:

template <typename... Args>
void logPrintf(const char *fmt, Args&&... args) {
    logOpen;
    fprintf(flog, fmt, args...);
    logClose;

    printf(fmt, args...);
}

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

答案 2 :(得分:-2)

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

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