重复使用变元函数参数不会;不起作用

Repeated use of a variadic function argument doesn't work

本文关键字:参数 不起作用 函数      更新时间:2023-10-16

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

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

原始代码失败,因为它试图在需要使用vprintf()的地方使用printf()。考虑到像logOpenlogClose语句这样的可疑点(给定符号,假设它们是打开和关闭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变量;只要在再次使用CCD_ 8之前使用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宏的每次调用应通过相同函数中CCD_ 20宏的相应调用来匹配。

由于logVprintf()函数不调用va_startva_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参数的版本。

升级编译器,这更像C++:

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

当然,提供CCD_ 34和CCD_。

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

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